<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Sam Ritchie</title>
 <link href="http://sritchie.github.com/feed/" rel="self"/>
 <link href="http://sritchie.github.com/"/>
 <updated>2014-12-09T19:09:20+00:00</updated>
 <id>http://sritchie.github.com/</id>
 <author>
   <name>Sam Ritchie</name>
   <email>sritchie09@gmail.com</email>
 </author>

 
 <entry>
   <title>New Blog!</title>
   <link href="http://sritchie.github.com/2014/12/09/new-blog/"/>
   <updated>2014-12-09T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2014/12/09/new-blog</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;Date - Boulder&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;New Blog&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
Hey all,
&lt;/p&gt;

&lt;p&gt;
I&#39;ve moved over to a Ghost blog at &lt;a href=&quot;http://www.samritchie.io&quot;&gt;samritchie.io&lt;/a&gt;. Check there for new posts. I&#39;ve mirrored the current content over and moved all Jekyll comments. See you at the new site.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Leadville Trail 100, 2014 Edition</title>
   <link href="http://sritchie.github.com/2014/08/20/leadville-trail-100-2014-edition/"/>
   <updated>2014-08-20T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2014/08/20/leadville-trail-100-2014-edition</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;20 August 2014 - Boulder&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Leadville Trail 100, 2014 Edition&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
Last weekend, I &lt;a href=&quot;http://www.strava.com/activities/181566715&quot;&gt;raced the Leadville Trail 100&lt;/a&gt; for the second time. Last year&#39;s race was physically brutal; I sat curled up at the 50 mile point, 11 pounds light and unable to keep down fluids, for almost two hours before rallying and banging out a strong second half for a finish of 26:15 (&lt;a href=&quot;http://www.strava.com/activities/75736093&quot;&gt;strava report&lt;/a&gt;). That race earned me the silver belt buckle awarded to all finishers under 30 hours:
&lt;/p&gt;

&lt;center&gt;
&lt;img width=&quot;400&quot; src=&quot;http://leadfeet.com/wp-content/uploads/2012/08/buckle.jpg&quot;/&gt;
&lt;/center&gt;

&lt;p&gt;
Badass, right? Not as badass as the ungodly huge GOLD belt buckle you get for finishing in under 25 hours.
&lt;/p&gt;

&lt;center&gt;
&lt;img width=&quot;400&quot; src=&quot;https://c1.staticflickr.com/7/6196/6078107288_efe5be2447_z.jpg&quot;/&gt;
&lt;/center&gt;

&lt;p&gt;
(Not sure whose crotch that is.) I knew that if I could figure out my nutrition issues, I could break the 25 hour barrier. Last winter, with this arbitrary goal spurring me on (I don&#39;t even have a belt that can support one of these), I registered again for the Leadville Trail 100.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;PreRace&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
A lot&#39;s changed in my life since last year. I quit my job at Twitter, founded &lt;a href=&quot;https://paddleguru.com&quot;&gt;PaddleGuru&lt;/a&gt; and moved to Boulder with &lt;a href=&quot;https://twitter.com/jennadawn&quot;&gt;Jenna&lt;/a&gt;. Running in the mountains instead of the streets of San Francisco has given me a huge confidence boost, though I&#39;ve been logging fewer road miles than last year, and Leadville has a good deal of flat&amp;#x2026; oh my god, did I screw up my preparation? I should have run more road!! Thoughts like those flared and faded all week. I guess it&#39;s impossible not to be anxious before a 100.
&lt;/p&gt;

&lt;p&gt;
My parents, my brother Mike and his girlfriend Katie flew in to Boulder the Sunday before the race. I ignored the race and showed off the town. We drove up to Twin Lakes on Monday to get used to the elevation get into Leadville zone. The cabin we rented had an unbelievable view of the town of Twin Lakes, mile 40 of the course, over which towered Hope Pass. It was hard to breathe, as expected, but Jenna and I had been up to Leadville for training runs this summer and weren&#39;t surprised. Here&#39;s the view from the cabin porch:
&lt;/p&gt;

&lt;center&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;Not a bad place to acclimatize for the &lt;a href=&quot;https://twitter.com/hashtag/LT100?src=hash&quot;&gt;#LT100&lt;/a&gt;. Good vibes to &lt;a href=&quot;https://twitter.com/sritchie&quot;&gt;@sritchie&lt;/a&gt;! &lt;a href=&quot;http://t.co/kWiFLemJST&quot;&gt;pic.twitter.com/kWiFLemJST&lt;/a&gt;&lt;/p&gt;&amp;mdash; jenna (@jennadawn) &lt;a href=&quot;https://twitter.com/jennadawn/statuses/499014120679956480&quot;&gt;August 12, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
This year I&#39;d be racing with three friends. Aaron Steele from Berkeley was flying out Friday to compete. I&#39;d met Michael O&#39;Brien after the Leadville Marathon, where he came in 4th (!!). Jason Antin is a fellow member of the &lt;a href=&quot;http://www.wwwright.com/climbing/minions/&quot;&gt;Satan&#39;s Minions scrambling club&lt;/a&gt; in Boulder and just an absolute crusher, gentleman and mountain man. Thinking of this as a &quot;competition&quot; was refreshing, as absurd as it sounds to race a hundred miler. Something to focus on besides the slog.
&lt;/p&gt;

&lt;p&gt;
I gave up on preparing my clothes and food early. My anxiety builds as the race gets closer, and packing up supplies the day before the gun goes off is soothing. As long as there are tasks, I don&#39;t have to think about the race. And I managed not to think about it, not to really acknowledge it, until the day before it started. I spent so much time this year telling Michael and Aaron about last year&#39;s race, about my lack of salt, about how you need to go SLOW, but it felt like a story I&#39;d read, not suffered through. Forced to think about it, I knew that I could BEGIN the race. I knew I could get to mile 40, walk slowly over Hope Pass, then see about coming home. Of course I could do that.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.strava.com/athletes/2028620&quot;&gt;Aaron Steele&lt;/a&gt; showed up Friday AM with my parents. He looked cool, just the consummate 100 miler. I&#39;d paced Aaron through an amazing 100 at the &lt;a href=&quot;http://ultrasignup.com/results_event.aspx?did=21327&quot;&gt;Rio del Lago&lt;/a&gt; last year, complete with a bonk at mile 72 and a HUGE surge in for a 22:35 finish. Dude&#39;s an animal.
&lt;/p&gt;

&lt;p&gt;
Aaron and I did a 30 minute shakeout on a nearby trail and I pointed out Hope Pass for the 15th time that week. I felt a goofy pride at being able to identify markers on the course, even markers as obvious as the enormous mountain pass looming above the town. My confidence faltered when a runner we saw on the trail told me that the right turn out of Twin Lakes I&#39;d been describing vividly to my parents - &quot;This is where you see the town as you roll downhill!&quot; - was actually a left turn. I think I&#39;ve been manufacturing memories of the course all year. Frightening.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;The Night Before&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
Aaron went to bed at 6pm. I stayed up for another couple of hours, packing, growing jittery, snapping at Jenna and my parents as they asked routine questions. The whole thing was getting to me. Finally, bedtime.
&lt;/p&gt;

&lt;p&gt;
I got up at 1am with Aaron for a pre-race breakfast of coffee, a bagel with Nutella and PB, a yogurt and a banana. I like to eat about 3 hours before the race to give my stomach time to empty. We couldn&#39;t stop giggling and woke the crew. Both of us had loopy dreams. I couldn&#39;t get back to sleep, so I laid in bed and sent out a burst of tweets about the race preparation. Backfire, as the sound of a Favorite interrupted sleep for my remaining hour.
&lt;/p&gt;

&lt;p&gt;
At 2:30 it was game time. Full body smearage of Aquaphor to prevent chafeage (and I mean full body - I hit the bathroom and slid INTO the toilet); short shorts, warm shirt, hat, fancy Salomon pack, the works. Into the car, to the startline, jacket to the parents, and there we were, lined up again in the dark at 4am. Oh my fucking god.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;Start to May Queen (13.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
My heart rate at the start last year fluttered around 100 and hit 120 with the gun. This year it held closer to 80.
&lt;/p&gt;

&lt;p&gt;
The gun goes and we&#39;re off, moving down the road toward May Queen, our first aid station at mile 13.5. Aaron and I started at the front and let the pros stream past us. Aaron let me pull ahead, but I was determined to start slow and ran the first few miles looking back over my shoulder, trying to keep him in sight as he snuck in and out of other runners. The headlamps behind us looked like fireflies and washed out all details of the trail. Betsy Kalmeyer, the woman I paced at this year&#39;s Hardrock, told me that in the mountains she sometimes mistakes these lights for stars.
&lt;/p&gt;

&lt;p&gt;
Aaron&#39;s plan was to run a really conservative race and hit Winfield at 12 hours, then cruise back for a strong, sub-25 finish. I was trying to break 11 hours into Winfield to match last year&#39;s pacing. If I could do that and subtract my 1:40 bonk, I could break 25 and get that big gold buckle.
&lt;/p&gt;

&lt;p&gt;
Two miles in I noticed &lt;a href=&quot;http://www.strava.com/activities/182377293&quot;&gt;Michael&lt;/a&gt; and our friend Patience to my right. Yes! Company! We talked about how happy we were to be going slow, how we weren&#39;t worried, and how great it was going to feel to reel in racers later in the race. Lots of bluster. It always feels like you&#39;re going too slow at the beginning. I pointed out spots to remember on the way back to give them hope during their finish sprints. A stump. Some random parking lot.
&lt;/p&gt;

&lt;p&gt;
On the dirt road 4 miles in, moving toward the first huge uphill, I noticed another runner ten feet off the trail, sitting against a tree, taking a shit and pointing his headlamp directly at his ass. Burlesque, baby! Someone&#39;s always gotta start strong.
&lt;/p&gt;

&lt;p&gt;
As we hit the uphill and get onto the single track around the lake the runners around us start to break out the stories. One old man tells me about his drop 10 years ago. He&#39;s been running hundreds ever since, and is finally prepared to attempt this one again. Later, on the single track, another guy tells me he&#39;s here because of a &lt;a href=&quot;https://www.ultrarunning.com/features/stepping-up-to-ultra/&quot;&gt;Dean Karnazes quote&lt;/a&gt; he&#39;d memorized:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&quot;If you want to run, run a mile. If you want to experience a different life, run a marathon. If you want to talk to God, run an ultra.&quot;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
I&#39;m not as fuzzy about the &quot;experience&quot; of running - I think those Talk to God moments are mostly because of mistakes in nutrition and pacing - but I smiled and told him he&#39;d come to the right place. I felt bad when I had to cut him off so I could wheel off into the woods and pee by headlamp. There&#39;s always an agenda to self-talk in a 100, my &quot;I feel great!&quot; chatter included. Really, I&#39;m so desperate to have someone to run with that each time I stop to pee, I run hard back up to Michael and Patience, risking ankle carnage on the rough single track.
&lt;/p&gt;

&lt;p&gt;
At the boat ramp, I saw my crew, hugged Jenna and Pretzel (my dog) and tried to mentally beam the message that this day is going to go BEAUTIFULLY, then on into the night again. I hit May Queen at mile 13.5 about ten minutes slower than last year and feeling calm.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;May Queen (13.5) to Outward Bound (23?)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
Last year I had to wait at May Queen for ten minutes while my crew scrambled to fill my pack with food. This year, I&#39;m ten minutes slower, but the crew was spot on. Such a great start to the day. I grabbed some snacks and a new bladder of Perpetuem from Jenna, hit the sunscreen and hustled up the road to join Michael for the hike up Sugarloaf. We started passing people right away on flat sections of trail, jumping around on rocks, keeping the heart rate low. A girl from New York City kept pace with us for a while and told us about her training runs in Central Park. Her boyfriend was pacing her, and he&#39;d already eaten a good amount of her supplies for breakfast by the time she&#39;d seen him at mile 13. Just a sad situation all around.
&lt;/p&gt;

&lt;p&gt;
She was moving and breathing way too hard for me to believe her stated goal of &quot;just finishing&quot;, but one thing I learned last year is that no one will be honest with you about their time goals. No need to tempt the race gods by declaring that you want to do go fast; better to stay humble and pretend you&#39;re just trying to sneak through the damned thing.
&lt;/p&gt;

&lt;p&gt;
The climb up Sugarloaf went by much faster than last year. I still walked almost every uphill, Now that I&#39;d seen the course I could relax and not wonder how much farther I had to go. My nerves about rolling the ankle I&#39;d sprained weeks before were fading! Michael and I talked to &lt;a href=&quot;http://barefootalex.tumblr.com/&quot;&gt;Barefoot Alex&lt;/a&gt;, a stud in huaraches and dreadlocks who seemed to know everyone on the course.
&lt;/p&gt;

&lt;p&gt;
Near the top of the climb, Alex said, &quot;it&#39;s funny, my fingers are swollen up like sausages!&quot; Holy shit. That&#39;s what happened to me last year. He confirmed that he&#39;d been peeing a lot all morning.
&lt;/p&gt;

&lt;p&gt;
&quot;Dude, you&#39;ve got to start taking salt pills.&quot; I pulled a couple out of my pack and pushed them into his huge hands like a salt evangelical. He was ME, and I had to fix him. He took the pills.
&lt;/p&gt;

&lt;p&gt;
Phew, crisis averted. I cruised down Powerline, moving through familiar territory. I pulled away from Michael a little on the downhill, trying to move fast but favor my quads. I passed Jason&#39;s girlfriend Jenny on the way down and asked where Jason was - she said he was WAY ahead and hammering. So impressive. There went my chances of winning our little group race.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-5&quot;&gt;Outward Bound (23) to Treeline (27.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-5&quot;&gt;
&lt;p&gt;
After a mile of quad-punishing descent, I bottomed out, crossed the creek and went up the short rise to the road that leads to Outward Bound and Treeline beyond. My parents hadn&#39;t seen Jason, so I figured he must be at least 45 minutes ahead.
&lt;/p&gt;

&lt;p&gt;
Jenna walked with me and I switched to a hand bottle with Perpetuem and some snacks; Michael caught up and we moved on together, acting as pacers for each other by now. The weather was nice and cool, the conversation was good, and BOOM, there was the next aid station! We were maybe 23 miles in now, but Ultramarathon time dilation had set in and it didn&#39;t feel long at all.
&lt;/p&gt;

&lt;p&gt;
&quot;Michael, we&#39;re halfway to halfway!&quot;, I yelled. &quot;Jokes&quot; like that are a nice way to check in on your condition. If you&#39;re doing well, they&#39;re not funny, but maybe worth a smile. If you&#39;re heading down a bad road, the phrase &quot;halfway to halfway&quot; makes you want to cry and tuck into a sleeping bag.
&lt;/p&gt;

&lt;p&gt;
The Perpetuem started tasting bad here. This had happened to me last year on the way up Hope Pass and signaled the start of terrible dehydration. Scared, I dumped the bottle at the aid station, filled it with water and at a PB&amp;amp;J. Crisis averted.
&lt;/p&gt;

&lt;p&gt;
The next bit is usually a long road stretch, but the organizers had subbed in traverse across a grassy field. Cheers had gone up when they announced the change at the pre-race meeting. I was excited, but when I hit the grass I realized that the field was pocked with ankle twisting bomb craters. It was just an absolutely fucked up horrible little pasture that cows couldn&#39;t even use. I dumped water on my head and moved through it, feeling strong without the backpack, loving the cool breeze, and tried not to think about how dangerous this stretch might be at night. I hit the Treeline just ahead of Michael and we moved out to Half Pipe together.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-6&quot;&gt;Treeline (27.5) to Twin Lakes (39.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-6&quot;&gt;
&lt;p&gt;
The Half Pipe aid station came up faster than I thought. So good! I was feeling the heat and eating a bunch of salt pills, measuring myself against last year&#39;s ghost. Time is hard to measure here in the trees. It&#39;s very hot, and there&#39;s nothing notable about the long road rollers and the slow, insidious uphill. This section is a great to stage a push on the way home, but not outbound. Michael and I puzzled over the riddle of the three prostitutes and the two condoms to pass the time. I dumped water on my head at every stream to battle what was looking to be a very, very hot day. Finally the downhill arrived.
&lt;/p&gt;

&lt;p&gt;
I rolled downhill behind a strong looking runner in a blue tanktop. His goal had been to finish sub 20, but he&#39;d been hit with intense diarrhea, forcing him to go slow.
&lt;/p&gt;

&lt;p&gt;
&quot;It&#39;s actually really great for ultras,&quot; he said. &quot;The diarrhea forces you to walk up hills and save up energy for that second half. Total blessing.&quot; Whatever you say, buddy. The state of his asshole bedamned, no way was I passing a guy whose time goal was five hours faster than mine. I stayed behind him into Twin Lakes. We hit the town stretch, and the watch confirmed what I&#39;d suspected - I was back on track with last year&#39;s schedule, only about five minutes behind!
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/M3bD6wZvaXa/embed/simple&quot; width=&quot;480&quot; height=&quot;480&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-7&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-7&quot;&gt;Twin Lakes (39.5) to Winfield (50.0)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-7&quot;&gt;
&lt;p&gt;
Last year I left Twin Lakes and immediately puked up the contents of my stomach. Melancholy set in on the way up Hope Pass, and I had to walk into Winfield, losing maybe an hour on my goal time. This year, my plan was to go easy and cross the Pass in great condition to run down. That alone would get me a half hour on least year; if I could get out of Winfield quickly and keep it together, I&#39;d be on track for a sub 25 finish.
&lt;/p&gt;

&lt;p&gt;
I brought my iPhone up Hope and listened to a &lt;a href=&quot;http://www.radiolab.org/story/birds/&quot;&gt;Radio Lab about this tiny population of whooping cranes&lt;/a&gt; and an old man with Alzheimer&#39;s. So sad. I was close to crying when I caught diarrhea boy and tried to tell him about the podcast. He didn&#39;t have much to say about the cranes, and didn&#39;t seem affected emotionally. What a brute.
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/M3giIbDgzEI/embed/simple&quot; width=&quot;480&quot; height=&quot;480&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
It was so hot. I dumped water on my head on the way up to keep the heart rate low and made it to the Llama field before the summit with my heart rate below 150, still calm. I pressed on through the aid station and past the bonfire. Near the top, one of the timers called out, &quot;Great to see a smile up here on the pass!&quot;. You got it, brother.
&lt;/p&gt;

&lt;p&gt;
When I topped out, I felt the urge to cry. I was so happy that I&#39;d made it up without feeling like death or puking. Reverse self pity!
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; &lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/M3gi70YWngz/embed/simple&quot; width=&quot;480&quot; height=&quot;480&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
Heart jackhammering, trying to settle the rate, I moved downhill fast, busting up my quads on the incredibly steep terrain, then turned up the horrible, deceptively uphill two miles of Colorado Trail that leads into Winfield. Stay cool, get to the halfway point.
&lt;/p&gt;

&lt;p&gt;
I saw Jason Antin on the way in, probably 30 minutes ahead of me. I wasn&#39;t thinking about catching him at this point. He gave me a bear hug and yelled &quot;Sam Ritchie!&quot;. I learned later that he&#39;d seen Jenna at the aid station and told her that I&#39;d be there soon - he could sense my presence on the trail. Not only is Jason an athletic beast, he&#39;s also a wizard. Here&#39;s Jason at the top of Hope Pass on his second crossing:
&lt;/p&gt;

&lt;center&gt;
&lt;img width=&quot;400&quot; src=&quot;https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-xfa1/t31.0-8/10623341_799684768429_1504081378802251267_o.jpg&quot;/&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-8&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-8&quot;&gt;Winfield (50.0) to Twin Lakes (59.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-8&quot;&gt;
&lt;p&gt;
I reached Winfield 10 hours and 33 into the race. Jenna ran out smiling and walked me in past my parents. My weight was stable, maybe two pounds down from my starting weight. Much better than 11 pounds last year. I ate some oranges, refilled my water, stuffed some ice into my headband. We were out within 12 minutes. BOOM! Suddenly I had almost 40 minutes on last year&#39;s time! I wasn&#39;t 11 pounds light and on my knees in the dirt! Such a celebration.
&lt;/p&gt;

&lt;p&gt;
I was feeling rough. Last year, even though puking outside the aid station tent wasn&#39;t exactly recovery, those two hours gave some minimal break to my stomach. This year I felt weak from the hot, hot sun. Jenna and I got to the Colorado trail and managed to keep up with last year&#39;s slow pace. The goal for the back 50 was to recreate what I&#39;d done last year. No need to go faster. If I could keep moving, I&#39;d be sub 25, earn my big arbitrary gold buckle and be free of the demons.
&lt;/p&gt;

&lt;p&gt;
My stomach started to revolt on the way up Hope. My heart rate hammered up to 150 at the left turn onto the Hope Pass trail and wouldn&#39;t settle. I walked, let downhill runners fly by and tried to keep my food down. Jenna kept pushing me to eat. &quot;How about just one Pretzel?&quot; Even these took a few minutes to choke down. It was really hard.
&lt;/p&gt;

&lt;p&gt;
The back side of Hope Pass has three phases. The woods, the boulder fields and the switchbacks. Each is about a mile long. Near the top of the woods phase we passed Jenny heading down. Jason had pushed, met her at the top of Hope Pass and proposed. &quot;He even put the ring on this cord since he knew my fingers would be so swollen!&quot; Everyone descending around her was smoked, favoring their quads and grimacing, but Jenny had the best smile on her face and was telling everyone she could about the engagement. She ended up finishing in 29:23. So awesome.
&lt;/p&gt;

&lt;p&gt;
We met Aaron Steele about halfway up the boulder fields. Aaron looked worked. I had been worried about his nutrition situation, and it was clear that he was a little behind, but smiling as always. It felt SO good to stop and talk and let my heart rate drop down to 115. I gave him a Honey Stinger bar, told him to stay cool and dunk his head in the stream, then pushed on. Jenna was dousing me with water by now, filling up our water bottle with stream water and dumping it all over my neck and arms. The heart rate kept climbing. We hit the section of trail where I&#39;d gotten the &lt;a href=&quot;http://www.strava.com/segments/2711011&quot;&gt;Strava CR&lt;/a&gt; weeks before&amp;#x2026; no chance of pushing anywhere close to that speed now.
&lt;/p&gt;

&lt;p&gt;
And then, finally, we were at the top among waving Tibetan prayer flags.
&lt;/p&gt;

&lt;center&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-conversation=&quot;none&quot; lang=&quot;en&quot;&gt;&lt;p&gt;.&lt;a href=&quot;https://twitter.com/palmajl&quot;&gt;@palmajl&lt;/a&gt; will do! We&amp;#39;ll be getting into night soon so the good vibes will be well taken! cc &lt;a href=&quot;https://twitter.com/sritchie&quot;&gt;@sritchie&lt;/a&gt; &lt;a href=&quot;http://t.co/S8Sp8Wws1b&quot;&gt;pic.twitter.com/S8Sp8Wws1b&lt;/a&gt;&lt;/p&gt;&amp;mdash; jenna (@jennadawn) &lt;a href=&quot;https://twitter.com/jennadawn/statuses/500810287336128512&quot;&gt;August 17, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
At the aid station, Jenna took selfies with the Llamas while I tried to eat noodles and drink water. I felt like puking every time we started running. God, is there no way to avoid this after 55 miles? Maybe my body could only function with a two hour break halfway. Maybe I couldn&#39;t get through a hundred miles without getting so fucked up I had to stop&amp;#x2026; the only question was where.
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/M3bDTPiV1h9/embed/simple&quot; width=&quot;480&quot; height=&quot;480&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
Cranking down the shaded trail on the north side of Hope, I focused on moving over the slippery roots and making good time as Jenna pushed behind me. Finally we were at the bottom, still in full daylight. My stomach allowed me to eat more pretzels near the bottom. I got another mental boost when I realized that we might beat last year&#39;s Hope crossing by 15 minutes.
&lt;/p&gt;

&lt;p&gt;
I&#39;d already clawed back a couple of hours on my time; if I could hang on, I might be able to push the 24 hour mark. On the other hand, aiming for too fast of a time goal could cause another blowup. Well, what the hell. I started getting excited about going faster.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-9&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-9&quot;&gt;Twin Lakes (59.5) to Treeline (71.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-9&quot;&gt;
&lt;p&gt;
I changed my shoes at Twin Lakes and picked up Eric Coppock, a fellow &lt;i&gt;minion&lt;/i&gt; who&#39;d volunteered to pace me the final 40. There was bad pain in a couple of my toenails, but I was prepared to lose a few and didn&#39;t look closely when I changed my socks.
&lt;/p&gt;

&lt;p&gt;
Water was still making me nauseous, but I loaded on on watermelon at the aid station, and DAMN was it good. Eric had a few bottles of Ensure in his pack; these had gotten me through the night last year.
&lt;/p&gt;

&lt;p&gt;
Out of Twin Lakes, you climb up the Colorado trail to the Mt Elbert trailhead through groves of enormous aspens. It&#39;s a huge climb, but runners only talk about Hope Pass and Powerline, so it can come as a slap in the face if you&#39;re not expecting it. In the evening, it&#39;s one of the most beautiful spots on the course.
&lt;/p&gt;

&lt;p&gt;
We hiked uphill. I described my day so far to Eric, trying to reassure myself by putting a positive spin on the tale. We&#39;d only run once together at Mt Sanitas in Boulder. Eric was calm about the stomach issues and suggested, over and over until I agreed, that I try the Ensure&amp;#x2026; and, YES, it stayed down! There could be hope after all. After half of the Ensure my stomach had settled and I was able to start drinking gulps of water again. We started jogging the small flat breaks in the climb.
&lt;/p&gt;

&lt;p&gt;
We hit the Mt Elbert mini aid station 3 miles up the hill, and the course began to flatten out. We started to crank downhill, hitting 8 or 9 minute mile pace. I needed a 15 minute mile average at this point to break 24 hours. It seemed daunting, but I knew that these long downhills would let us put a few minutes in the bank for the later climbs. Every 11 minute mile that passed was encouraging.
&lt;/p&gt;

&lt;p&gt;
Running, drinking Ensure, drinking water. Trying to get a bottle an hour down. This was my life. We started passing people. The sun was still up, unlike last year, and I could run in a t-shirt and feel warm. My heart rate was down to 140 now that I was out of the heat. All I had to do now was deliver and use this excellent terrain to gain time before the sun set and the cold forced me to slow down.
&lt;/p&gt;

&lt;p&gt;
To our right was the most beautiful sight I&#39;ve ever seen in the mountains. I don&#39;t have a picture, and can&#39;t find one, but the mountains to the East were blood red. A storm was pouring rain in the distance, and ruby shadows flickered in the enormous column of water pouring down over the hills. I yelled to the runners ahead, &quot;LOOK TO THE RIGHT!&quot;. Some turned and hooted, others kept marching, grimaces untouched. Hey, I tried.
&lt;/p&gt;

&lt;p&gt;
At the Half Pipe aid station, I grabbed more watermelon and we moved out for the last two miles to Treeline. We took a walking break and prepped the headlamps. Eric&#39;s was this wild homemade deal with no on/off toggle that he clipped to his belt. It was incredibly bright, and seemed to give him energy. I think it was some sort of Ironman core.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-10&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-10&quot;&gt;Treeline (71.5) to Powerline (79.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-10&quot;&gt;
&lt;p&gt;
It was dark when we hit Treeline with its line of cars. Treeline was quiet; we were going fast enough now that we were ahead of the 25 hour press, so runners were only coming through every couple of minutes. My parents, my beautiful parents, had COFFEE. Oh my god, so good. I clutched it and sucked it down like Gollum slurping fish guts. Jenna ran out with me, then realized after a few minutes that she hadn&#39;t brought a headlamp. Eric and I moved down the trail while Jenna stumbled back to the cars in the dark. Whoops.
&lt;/p&gt;

&lt;p&gt;
The road section was hard. I wanted to slow down, but Eric was telling stories and keeping a fast pace, so to be polite I kept running and talking. Soon we hit the awful, cratered field near Outward Bound and slowed to a walk to pick our way around the rocks and bomb holes. I tried to run a little, but stuck my foot in one hole and snapped my knee backward, then did it again and almost rolled my weak right ankle. Enough. We walked into Outward Bound, fueled on watermelon and moved out toward the bottom of Powerline, where Jenna&#39;s brother &lt;a href=&quot;http://www.strava.com/activities/181299213&quot;&gt;Adam&lt;/a&gt; was waiting for his first pacing leg ever. Yesterday Adam had told me he was worried that he wasn&#39;t going to be able to keep up with me. With 80 miles in my legs, I knew he wouldn&#39;t have a problem.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-11&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-11&quot;&gt;Powerline (79.5) to May Queen (86.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-11&quot;&gt;
&lt;p&gt;
I turned down the road to Powerline with Adam and Eric. Eric wanted all the training he could get for the Run Rabbit Run 100 later this month. His justification: &quot;Well, it&#39;s going to be hard to start running again if I sit in the warm car for a couple of hours, so I&#39;ll just head over the summit with you guys.&quot; The stud factor was high.)
&lt;/p&gt;

&lt;p&gt;
We power hiked every step of the uphill at a steady, 17 minute mile pace. My heart rate moved between 125 and 140. We were passing runners, jogging every short flat, downing Ensures. Powerline is the land of a 1000 false summits. Every few minutes, you think you&#39;ve topped out when another green trail-marking glowstick looms high above you and you realize that no, this flat is only here to deceive you, the trail winds up and up.
&lt;/p&gt;

&lt;p&gt;
At the top of Powerline (the last climb conquered!) we ran into an impromptu aid station of blazed guys and girls with glowsticks, water, Coke and a board spraypainted with the words, &quot;YOU FUCKING ROCK!&quot;. Eric refilled our bottles and Adam and I moved on. Behind me I heard some girls whispering, &quot;you have to slap his ass as he leaves!&quot; right before a girl in a skeleton shirt and a glow stick necklace ran up and whacked Adam. His first pacing experience was going all right.
&lt;/p&gt;

&lt;p&gt;
I didn&#39;t know how my feet were moving so fast down the rutted, rocky jeep trail. I felt disconnected from them in the wash of the headlamp. I looked down at my feet and saw them jumping over rocks and turning over at high cadence. I had no understanding of how I was making that happen.
&lt;/p&gt;

&lt;p&gt;
I thought about how many steps I&#39;d taken so far and felt a little sick. Just keep going. Finally, the flat section of Hagerman road. I ate a gel before the final, bombing single track downhill, the last significant downhill on the course. It felt endless, but Adam was so happy - &quot;Sick!!&quot; - that I had to smile and gutted it out. The technical downhill of this section required an IQ level that was beyond my reach. Keep drinking water, keep moving.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-12&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-12&quot;&gt;May Queen (86.5) to Leadville (100.0)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-12&quot;&gt;
&lt;p&gt;
We hit May Queen over an hour ahead of the schedule I&#39;d set out for my parents, and left the aid station just one minute before they arrived. Adam found a bar of service and called Jenna. &quot;We&#39;re leaving May Queen.&quot; I could hear &quot;What??&quot; on the other end of the line, and then the service cut out.
&lt;/p&gt;

&lt;p&gt;
At this point, my brain could understand the remaining distance. A half marathon, nothing crazy. I knew Jason was ahead of me, probably more than thirty minutes. These are dangerous thoughts with hours left, but I thought that it MIGHT be possible to catch him at the end if I pushed hard. Let&#39;s see.
&lt;/p&gt;

&lt;p&gt;
The first four miles of lake is very technical in the dark. I had some hot chocolate at the aid station, which raised the IQ by a couple of points, and then we were back at it, walking the uphills, running the down, switching gears every few seconds at times. Don&#39;t think about how much time is left, just run.
&lt;/p&gt;

&lt;p&gt;
We could hear the boat ramp, and then there it was! I was just a total asshole by this point. Jenna asked me, &quot;Wow, how did you take an hour off your time?&quot;
&lt;/p&gt;

&lt;p&gt;
&quot;By running fast, obviously&amp;#x2026; how did you guys take two hours to get May Queen?&quot; Ouch. It&#39;s embarrassing to think about now. I was at a dark place, desperate to get the final Ensure and two gels and leave. We dropped Adam off and kept moving fast, passing a couple more runners. Eric was quiet and efficient, offering water, holding me back.
&lt;/p&gt;

&lt;p&gt;
Oh, man, this was it. A 12 minute mile average MIGHT be able to get me in in under 23 hours. I knew that I could hammer the last five miles, but was the pain worth it? I decided I could at least try, and if I bonked and walked it in still get a time closer to 23 hours than 24, far faster than I&#39;d ever expected. Okay. Keep eating. Almost game time.
&lt;/p&gt;

&lt;p&gt;
We hit the rocky downhill off the lake. My nerves had calmed. I knew my final push was going to start at the bottom. I kept drinking Ensure. The calories had to be there for this part. I could see my breath and relaxed down the hill, ignoring my swollen hip flexors, ignoring the throbbing pain in my toes. We hit the dirt and started running, really running. My technique felt flawless, though it probably looked terrible. We dropped down to an 8:30 pace, and started to gain on the headlamps in front of us. Eric has his own 100 miler coming up and decided not to get too wild; after a mile of our insane push, he handed me the water bottle and a gel and sent me off to the finish. I knew the markers now. I knew I could go fast. Every minute below a 12 minute mile would bank me time if I bonked.
&lt;/p&gt;

&lt;p&gt;
I was flying, slowing down on the bumpy dirt road by the train tracks, taking in a gel at the bottom of the turn to the last big uphill, hiking hard over the rocks, three miles to go, maybe a little more, don&#39;t think about how long that ACTUALLY takes - three miles is nothing! - I stopped looking at the heart rate monitor and watched my breath fog ahead of me. Headlamps flared up as pacers looked back at the noise. Everyone was wonderful, cheering for me as I passed. &quot;Finish strong, man! Yeah!&quot; Two miles to go, now. Don&#39;t think, don&#39;t look at the watch, just wait for the road and gun it.
&lt;/p&gt;

&lt;p&gt;
Then, finally, I could see pavement. I hit the last dirt uphill and turned left onto the road, alone now, no fire in me for a final sprint but going as fast as last year. I&#39;d been going this fast for miles, now, still down at 8:20 pace. Up the final rise, and I could hear the finish line now&amp;#x2026; and then, holy shit, ahead of me, could it be Jason? I recognized him by his huge calves. I didn&#39;t care about catching him anymore, just about finishing and fighting off the quitting bargain my brain was trying to make.
&lt;/p&gt;

&lt;p&gt;
It was Jason! He saw me passing on the left and yelled, &quot;Is that Sam? Is that Sam Ritchie? You&#39;re killing it, man! You&#39;re killing it!&quot; So, so good to hear those words. We had maybe a half mile to go, and I could see the finish line, but I couldn&#39;t make sense of the patterns of lights. My vision started to wobble and I forced it steady, drinking water, not letting myself fade, trying to stay fast and steady uphill&amp;#x2026;. and there it was, the clock at, holy fuck, 22:38! Up the red carpet, looking for my parents and Jenna, hoping they hadn&#39;t missed the finish&amp;#x2026; and there they were. Finally, finally, I could stop. I stopped. I was sore, happy, and mentally there. And finally, finished.
&lt;/p&gt;

&lt;p&gt;
Later, I found out that I&#39;d gotten third in my age group. This is completely surreal to me. I used to be a kayaker, then a crossfitter. The running realm is intimidating. But whatever happens now, Leadville 2014 was an amazing time out in the mountains. Here&#39;s a shot Jenna took at the awards ceremony:
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/M312uejhl3T/embed/simple&quot; width=&quot;480&quot; height=&quot;480&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
And, of course, the bounty:
&lt;/p&gt;

&lt;center&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/hashtag/LT100?src=hash&quot;&gt;#LT100&lt;/a&gt; Bounty from this year! Glad I didn’t have to fly… &lt;a href=&quot;http://t.co/tL0SGTqZ1h&quot;&gt;pic.twitter.com/tL0SGTqZ1h&lt;/a&gt;&lt;/p&gt;&amp;mdash; Sam Ritchie (@sritchie) &lt;a href=&quot;https://twitter.com/sritchie/statuses/501537360668930048&quot;&gt;August 19, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
UPDATE: Here&#39;s a fun &lt;a href=&quot;https://storify.com/sritchie/leadville-100-2014&quot;&gt;Storify&lt;/a&gt; with social media from the day! Thanks to my lovely crew for firing these off while I raced.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>API Authentication with Liberator and Friend</title>
   <link href="http://sritchie.github.com/2014/01/17/api-authentication-with-liberator-and-friend/"/>
   <updated>2014-01-17T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2014/01/17/api-authentication-with-liberator-and-friend</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;Jan 17 2014 - Boulder&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;API Authentication with Liberator and Friend&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
I&#39;ve just finished rewriting a number of &lt;a href=&quot;https://paddleguru.com&quot;&gt;PaddleGuru&lt;/a&gt;&#39;s internal APIs using two great open-source libraries; &lt;a href=&quot;http://clojure-liberator.github.io/liberator/&quot;&gt;Liberator&lt;/a&gt; and &lt;a href=&quot;https://github.com/cemerick/friend&quot;&gt;Friend&lt;/a&gt;. Liberator is a library for writing RESTful resources in Clojure. Friend is an authorization and authentication library written by the prolific Chas Emerick, Dominator, Esquire. You&#39;ve certainly seen his stuff around if you&#39;ve played with Clojure(Script) in any level of detail.
&lt;/p&gt;

&lt;p&gt;
Authentication and authorization are both really important in RESTful APIs. These libraries are made for each other, I thought to myself. I&#39;ll just use them together and life will be wonderful. Right?
&lt;/p&gt;

&lt;p&gt;
Well, not so much. Friend and Liberator both have opinionated approaches to authentication. Integrating the two was trickier than I imagined, and required a bit of code massage.
&lt;/p&gt;

&lt;p&gt;
In this post, I&#39;ll give a short overview of Liberator and a not-so-short overview of Friend. (Friend&#39;s more confusing, and needs a longer treatment.) I&#39;ll discuss how each of these libraries deals with authorization and authentication, and walk through an example project I built that demonstrates how PaddleGuru uses Friend and Liberator in concert to define RESTful API endpoints with really nice, consistent authorization and authentication handling.
&lt;/p&gt;

&lt;p&gt;
The code for the example project is up on &lt;a href=&quot;https://github.com/paddleguru/liberator-friend&quot;&gt;GitHub&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;Liberator&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
Liberator lets you define a RESTful resource as a graph of decision points and responses (represented as key-value pairs in Clojure). It handles content negotiation, resource caching, all of the tough stuff that you end up doing as nested function calls without a library like Liberator.
&lt;/p&gt;

&lt;p&gt;
From the &lt;a href=&quot;http://clojure-liberator.github.io/liberator/&quot;&gt;project site&lt;/a&gt;:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Liberator is a Clojure library that helps you expose your data as resources while automatically complying with all the relevant requirements of the HTTP specification (RFC-2616). Your resources will automatically gain useful HTTP features, such as caching and content negotiation. Liberator was inspired by Erlang’s Webmachine. By following the constraints and requirements in RFC-2616, liberator will enable you to create application according to a REST architecture.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Here&#39;s a simple resource that handles only HTML responses, and returns &lt;code&gt;406 Not Acceptable&lt;/code&gt; if the user requests some other content type:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defresource&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;hello-resource&lt;/span&gt;
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:available-media-types&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/html&quot;&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:handle-ok&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;&amp;lt;html&amp;gt;Hello, Internet.&amp;lt;/html&amp;gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Because a resource is a function of a request, you can use resources with Compojure like this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;ANY&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/foo&quot;&lt;/span&gt; [] hello-resource&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Check out the &lt;a href=&quot;http://clojure-liberator.github.io/liberator/&quot;&gt;project page&lt;/a&gt; for more tutorials and documentation.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;Friend&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
Friend provides Ring middleware that handles authentication and authorization for your app. (&quot;Authentication&quot; is whether or not the system knows who you are; authorization is whether or not you&#39;re allowed in to a particular resource, one the system identifies you.)
&lt;/p&gt;

&lt;p&gt;
The middleware looks like this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;ns&lt;/span&gt; liberator-friend.middleware.auth
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:require&lt;/span&gt; [cemerick.friend &lt;span style=&quot;color: #008b8b;&quot;&gt;:as&lt;/span&gt; friend]
            &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;cemerick.friend [workflows &lt;span style=&quot;color: #008b8b;&quot;&gt;:as&lt;/span&gt; workflows]
                             [credentials &lt;span style=&quot;color: #008b8b;&quot;&gt;:as&lt;/span&gt; creds]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;friend-middleware&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Returns a middleware that enables authentication via Friend.&quot;&lt;/span&gt;
  [handler users]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [friend-m {&lt;span style=&quot;color: #008b8b;&quot;&gt;:credential-fn&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;partial&lt;/span&gt; creds/bcrypt-credential-fn users&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                  &lt;span style=&quot;color: #008b8b;&quot;&gt;:workflows&lt;/span&gt;
                  [&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;workflows/http-basic &lt;span style=&quot;color: #008b8b;&quot;&gt;:realm&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                   &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;workflows/interactive-form&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;]}]
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;-&amp;gt;&lt;/span&gt; handler
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;friend/authenticate friend-m&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This middleware sits over top of all of your resources and routing layer (typically handled with a library like &lt;a href=&quot;https://github.com/weavejester/compojure&quot;&gt;Compojure&lt;/a&gt;), and provides all of the plumbing necessary for authorization and authentication.
&lt;/p&gt;

&lt;p&gt;
(Okay, this is where it gets confusing. For me, anyway. There&#39;s a lot of indirection to keep track of in the API. Follow me as best you can, and supplement with Friend&#39;s &lt;a href=&quot;https://github.com/cemerick/friend&quot;&gt;extensive documentation&lt;/a&gt;.)
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-2-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-2-1&quot;&gt;Authentication&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-2-1&quot;&gt;
&lt;p&gt;
Friend&#39;s &quot;workflows&quot; provide pluggable authentication for your app.
&lt;/p&gt;

&lt;p&gt;
Friend considers a request to be authenticated if the incoming request&#39;s session has a certain special key:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;{&lt;span style=&quot;color: #008b8b;&quot;&gt;:session&lt;/span&gt; {&lt;span style=&quot;color: #008b8b;&quot;&gt;::cemerick.friend/identity&lt;/span&gt; &amp;lt;user&#39;s identity!&amp;gt;}}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
(That&#39;s a &lt;a href=&quot;https://kotka.de/blog/2010/05/Did_you_know_III.html&quot;&gt;namespace-qualified keyword&lt;/a&gt;, by the way.)
&lt;/p&gt;

&lt;p&gt;
Friend&#39;s middleware examines every incoming request for this key. If the key is present, Friend passes the request on, no problem. (If you&#39;re using a session store, this will prevent your app from having to run through the login workflows on every request).
&lt;/p&gt;

&lt;p&gt;
If &lt;code&gt;::cemerick.friend/identity&lt;/code&gt; is missing from the session, Friend&#39;s middleware attempts to authenticate the session using its workflows. The middleware passes the request into each workflow in turn until one kicks out a return value, or all have returned &lt;code&gt;nil&lt;/code&gt;. Only then will the middleware pass your request on.
&lt;/p&gt;

&lt;p&gt;
Let&#39;s talk about the supported return values. Workflows can return one of three things:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;nil&lt;/b&gt;: &lt;code&gt;nil&lt;/code&gt; means that the workflow has no nothing to say about the supplied request. Friend will send the request to the next workflow in the list, if one exists. If no workflows are left, Friend calls your handler.
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Friend Auth&lt;/b&gt;: This is ANY Clojure map with a type of &lt;code&gt;::cemerick.friend/auth&lt;/code&gt;. The default workflows try to authenticate a user using the &lt;code&gt;:credential-fn&lt;/code&gt; you supplied to the middleware (see my above example). If &lt;code&gt;:credential-fn&lt;/code&gt; returns a map, the default workflows interpret it as a user record, associate the &lt;code&gt;::cemerick.friend/auth&lt;/code&gt; type metadata, merge the identity into the request under &lt;code&gt;{:session {::cemerick.friend/identity &amp;lt;returned auth map&amp;gt;}}&lt;/code&gt; and call your hander with the updated, authenticated request.
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Anything else&lt;/b&gt;: Any other response is treated as a ring response, and passed back immediately. Your handler is never called.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Let&#39;s look at an example workflow to see how it handles these three cases. In my snippet above I included Friend&#39;s &lt;code&gt;http-basic&lt;/code&gt; workflow:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;require&lt;/span&gt; &#39;cemerick.friend.workflows&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;workflows/http-basic &lt;span style=&quot;color: #008b8b;&quot;&gt;:realm&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
(&lt;a href=&quot;https://github.com/cemerick/friend/blob/master/src/cemerick/friend/workflows.clj#L35&quot;&gt;Here&#39;s the code&lt;/a&gt; if you want to follow along.)
&lt;/p&gt;

&lt;p&gt;
When this workflow sees a request, it first checks the request for an &lt;code&gt;authorization&lt;/code&gt; header. If that header&#39;s missing, it returns &lt;code&gt;nil&lt;/code&gt;, and Friend proceeds to the next workflow, or lets the request through.
&lt;/p&gt;

&lt;p&gt;
If the &lt;code&gt;authorization&lt;/code&gt; header IS present, the workflow extracts the supplied username and password and passes them in to &lt;code&gt;:credential-fn&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
If this check succeeds (ie, returns something non-nil), the workflow returns the required &lt;code&gt;::cemerick.friend/identity&lt;/code&gt; key described above. If it fails, the workflow returns &lt;a href=&quot;https://github.com/cemerick/friend/blob/master/src/cemerick/friend/workflows.clj#L58&quot;&gt;a failing ring response&lt;/a&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;{&lt;span style=&quot;color: #008b8b;&quot;&gt;:status&lt;/span&gt; 400
 &lt;span style=&quot;color: #008b8b;&quot;&gt;:body&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Malformed Authorization header for HTTP Basic authentication.&quot;&lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
You can use these three response types to implement some pretty interesting authentication workflows.
&lt;/p&gt;

&lt;p&gt;
You can do a lot in this framework. &lt;a href=&quot;https://github.com/ddellacosta&quot;&gt;Ddellacosta&lt;/a&gt;&#39;s &lt;a href=&quot;https://github.com/ddellacosta/friend-oauth2&quot;&gt;Friend OAuth2 workflow&lt;/a&gt; intercepts the initial OAuth request and uses the &quot;failure&quot; return to send out an OAuth2 redirect to the configured provider. When the provider redirects back to the app, the OAuth2 workflow again intercepts the command, does token negotation, then either succeeds or fails the response. Two intercepts! There&#39;s a lot going on there.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-2-2&quot;&gt;Authorization&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-2-2&quot;&gt;
&lt;p&gt;
Okay, phew. That covers authentication. Now we need to talk about authorization, or protecting your resources.
&lt;/p&gt;

&lt;p&gt;
Resources typically handle authorization with some function of the &lt;code&gt;::friend/identity&lt;/code&gt; that the middleware added to the session. If the identity is missing (IE, the request isn&#39;t authenticated) or the identity doesn&#39;t have the required permissions, the resource can throw an exception with &lt;a href=&quot;https://github.com/cemerick/friend/blob/master/src/cemerick/friend.clj#L256&quot;&gt;cemerick.friend/throw-unauthorized&lt;/a&gt; with the identity that didn&#39;t pass the check. (This might be nil, of course.) This function throws an exception with some special metadata.
&lt;/p&gt;

&lt;p&gt;
Friend&#39;s middleware is wrapping the entire app, and catches exceptions with this special metadata as they bubble up. Once this happens, Friend takes responsibility for the response with one of two actions:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;If Friend sees that the user is authenticated, it calls &lt;code&gt;:unauthorized-handler&lt;/code&gt;. (You supply this option when you create the middleware. This is where you&#39;d return some sexy, custom page, or redirect to the home page with a flash yelling &quot;You&#39;re not authorized!&quot;). You can include custom info in the thrown exception to make that flash all custom and sexy.
&lt;/li&gt;
&lt;li&gt;If the request is NOT authenticated (no &lt;code&gt;::friend/identity&lt;/code&gt; in the session), Friend calls the &lt;code&gt;:unauthenticated-handler&lt;/code&gt;. By default, this stores the URI the request was originally trying to access in the session map and redirects the user to your login page.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Now, in the latter, unauthenticated case, Friend typically redirects to a route that&#39;s being watched by one of the workflows. Friend&#39;s supplied &lt;code&gt;interactive-form&lt;/code&gt; workflow does this; it redirects to a URI like &quot;/login&quot;, then intercepts POST requests to &quot;/login&quot; and tries to pull out credentials and authenticate.
&lt;/p&gt;

&lt;p&gt;
Once you&#39;re authenticated (and this is a new thing I didn&#39;t mention above), if the session has any record of the URI you were trying to access when the app threw the &lt;code&gt;unauthorized!&lt;/code&gt; exception, Friend will BREAK from the pattern I mentioned above and instead redirect to that stored URI. This gives the resource another chance to check your (now populated) credentials.
&lt;/p&gt;

&lt;p&gt;
If you make it through, great. If the resource throws an exception again, Friend will catch it again, but this time take the first branch and call &lt;code&gt;:unauthorized-handler&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
I find all that throwing and catching to be extremely confusing. I&#39;m not really sure how to clean it up, but please, please let me know if you have ideas after ingesting all of this.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;Combining Friend and Liberator&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
Liberator has a decision point to deal with authorization and authentication: &lt;code&gt;:authorized?&lt;/code&gt;. You provide a predicate for the &lt;code&gt;:authorized?&lt;/code&gt; key in your resource definition, and Liberator will either call its &lt;code&gt;:handle-unauthorized&lt;/code&gt; handler (on false) or proceed down the decision tree (on true).
&lt;/p&gt;

&lt;p&gt;
After figuring out Friend and absorbing all of the intricate subtleties described above, it became clear to me that a single predicate was NOT enough for really good auth. Rather than rolling my own session management, redirect handlers, etc, I had to figure out how to use the two libraries together.
&lt;/p&gt;

&lt;p&gt;
My main blocker here was that Liberator didn&#39;t allow resources to inherit key-value pairs from other resources. The resources are effectively maps, and you should be able to define a base map of decision points and then merge them together.
&lt;/p&gt;

&lt;p&gt;
So I wrote &lt;a href=&quot;https://github.com/clojure-liberator/liberator&quot;&gt;a pull request&lt;/a&gt; that extends Liberator&#39;s resources to accept a &lt;code&gt;:base&lt;/code&gt; key. The &lt;code&gt;:base&lt;/code&gt; key takes a map of liberator decision points and creates your resource by merging the other kv pairs into these defaults.
&lt;/p&gt;

&lt;p&gt;
This pull req allows you to define a base resource like this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;base-resource&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Base for all resources.&lt;/span&gt;

&lt;span style=&quot;color: #8b2252;&quot;&gt;   Due to the way liberator&#39;s resources merge, these base definitions&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;   define a bunch of content types, even if the resources that inherit&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;   from them don&#39;t. The defaults are here to provide reasonable text&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;   error messages, instead of returning big slugs of html.&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [not-found &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;comp&lt;/span&gt; rep/ring-response
                        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;route/not-found &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Route not found!&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
        base {&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/html&quot;&lt;/span&gt; not-found}]
    {&lt;span style=&quot;color: #008b8b;&quot;&gt;:handle-not-acceptable&lt;/span&gt;
     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;-&amp;gt;&amp;gt;&lt;/span&gt; {&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;application/json&quot;&lt;/span&gt; {&lt;span style=&quot;color: #008b8b;&quot;&gt;:success&lt;/span&gt; false
                               &lt;span style=&quot;color: #008b8b;&quot;&gt;:message&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;No acceptable resource available&quot;&lt;/span&gt;}
           &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/plain&quot;&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;No acceptable resource available.&quot;&lt;/span&gt;}
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;with-default &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;media-typed base&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

     &lt;span style=&quot;color: #008b8b;&quot;&gt;:handle-not-found&lt;/span&gt;
     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;-&amp;gt;&amp;gt;&lt;/span&gt; {&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;application/json&quot;&lt;/span&gt; {&lt;span style=&quot;color: #008b8b;&quot;&gt;:success&lt;/span&gt; false
                               &lt;span style=&quot;color: #008b8b;&quot;&gt;:message&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Resource not found.&quot;&lt;/span&gt;}
           &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/plain&quot;&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Resource not found.&quot;&lt;/span&gt;}
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;with-default &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;media-typed base&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And then write other resources that extend the base like so:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defresource&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;hello-resource&lt;/span&gt;
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:base&lt;/span&gt; base-resource
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:allowed-methods&lt;/span&gt; [&lt;span style=&quot;color: #008b8b;&quot;&gt;:get&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:available-media-types&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:handle-ok&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Welcome to the resource!&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This tiny resource now shares the &lt;code&gt;:handle-not-acceptable&lt;/code&gt; and &lt;code&gt;:handle-not-found&lt;/code&gt; behavior from the base. If I hit the resource and ask for JSON, for example, I&#39;ll get a &quot;No acceptable resource available.&quot; message in plain-text. (There&#39;s more work here to make this perfect, but hey, it&#39;s a start.)
&lt;/p&gt;

&lt;p&gt;
Check out my customer version of &lt;code&gt;defresource&lt;/code&gt; in the post&#39;s &lt;a href=&quot;https://github.com/paddleguru/liberator-friend/blob/master/src/liberator_friend/resources.clj#L67&quot;&gt;example project&lt;/a&gt;. That namespace also contains &lt;code&gt;base-resource&lt;/code&gt; and all the helper functions.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-3-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-3-1&quot;&gt;Authenticating Resources&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-3-1&quot;&gt;
&lt;p&gt;
&lt;a href=&quot;https://github.com/paddleguru/liberator-friend&quot;&gt;liberator-friend&lt;/a&gt; that shows off my final solution: Liberator resources that delegate to Friend into the &lt;code&gt;authorized?&lt;/code&gt; point. The code is on &lt;a href=&quot;https://github.com/paddleguru/liberator-friend&quot;&gt;GitHub&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
The example project defines a &lt;a href=&quot;https://github.com/paddleguru/liberator-friend/blob/master/src/liberator_friend/resources.clj#L218&quot;&gt;Friend base resource&lt;/a&gt; that provides a handler that Liberator calls when &lt;code&gt;:authorized?&lt;/code&gt; returns false:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;friend-resource&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Base resource that will handle authentication via friend&#39;s&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  mechanisms. Provide an authorization function and you&#39;ll be good to&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  go.&quot;&lt;/span&gt;
  {&lt;span style=&quot;color: #008b8b;&quot;&gt;:base&lt;/span&gt; base-resource
   &lt;span style=&quot;color: #008b8b;&quot;&gt;:handle-unauthorized&lt;/span&gt;
   &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;media-typed {&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/html&quot;&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;fn&lt;/span&gt; [req]
                               &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;unauthorized!
                                &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;-&amp;gt;&lt;/span&gt; req &lt;span style=&quot;color: #008b8b;&quot;&gt;:resource&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:allowed?&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                                req&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
                 &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;application/json&quot;&lt;/span&gt;
                 {&lt;span style=&quot;color: #008b8b;&quot;&gt;:success&lt;/span&gt; false
                  &lt;span style=&quot;color: #008b8b;&quot;&gt;:message&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Not authorized!&quot;&lt;/span&gt;}
                 &lt;span style=&quot;color: #008b8b;&quot;&gt;:default&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;constantly&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Not authorized.&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;friend-resource&lt;/code&gt; extends &lt;code&gt;base-resource&lt;/code&gt; from above, just for fun. The &lt;code&gt;unauthorized!&lt;/code&gt; function above is &lt;a href=&quot;https://github.com/paddleguru/liberator-friend/blob/master/src/liberator_friend/resources.clj#L211&quot;&gt;also mine&lt;/a&gt;; it pulls the &lt;code&gt;::friend/identity&lt;/code&gt; key out of the request, and also sends the function representing next step in the Liberator decision tree up to Friend&#39;s middleware. (If the user&#39;s not authenticated, this lets Friend workflows perform auth with a database, then jump BACK into Liberator&#39;s decision tree at the &lt;code&gt;allowed?&lt;/code&gt; stage to try again. Pretty awesome.
&lt;/p&gt;

&lt;p&gt;
That covers the Friend middleware integration. Now all we need to do is override &lt;code&gt;:authorized?&lt;/code&gt; on each resource to return true or false, and everything else will just work. I wrote a few helpers that make it easy to test Friend&#39;s identity map in Liberator&#39;s &lt;code&gt;authorized?&lt;/code&gt; function:
&lt;/p&gt;

&lt;p&gt;
This resource extends the base resource, but adds in a default unauthorized handler. This is all Friend needs - if the user&#39;s unauthorized, either handle it immediately, OR, in the HTML case (assuming browsers always access via HTML), the resource throws the proper redirect.
&lt;/p&gt;

&lt;p&gt;
Now all we need to do is override &lt;code&gt;:authorized?&lt;/code&gt; on each resource to return true or false, and everything else will just work.
&lt;/p&gt;

&lt;p&gt;
I wrote a helper function that defines nice authorization predicates based on Friend&#39;s concept of a &lt;code&gt;role&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;roles&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Returns an authorization predicate that checks if the authenticated&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  user has the specified roles. (This is the usual friend behavior.)&quot;&lt;/span&gt;
  [roles]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;fn&lt;/span&gt; [id]
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;friend/authorized? roles id&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This function creates a new base resource that extends &lt;code&gt;friend-resource&lt;/code&gt; above, adding in the supplied authorization function:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;friend-auth&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Returns a base resource that authenticates using the supplied&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  auth-fn. Authorization failure will trigger Friend&#39;s default&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  unauthorized response.&quot;&lt;/span&gt;
  [auth-fn] {&lt;span style=&quot;color: #008b8b;&quot;&gt;:base&lt;/span&gt; friend-resource
             &lt;span style=&quot;color: #008b8b;&quot;&gt;:authorized?&lt;/span&gt; auth-fn}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Those two helpers work together to create Friend-aware (Friend-ly?) base resource generators. All resources that use these bases will be protected by the Friend middleware. In the example project, this means that they&#39;ll be protected with HTTP basic authentication, but you can add more workflows to perform different auth in a way that doesn&#39;t require you to rewrite your resources.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;role-auth&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Returns a base resource that authenticates users against the&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  supplied set of roles.&quot;&lt;/span&gt;
  [role-input]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;friend-auth &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;comp&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;roles role-input&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:request&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;authenticated-base&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Returns a base resource that authenticates users against the&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  supplied set of roles.&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;friend-auth &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;comp&lt;/span&gt; boolean friend/identity &lt;span style=&quot;color: #008b8b;&quot;&gt;:request&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The first, &lt;code&gt;role-auth&lt;/code&gt;, takes a set of roles and allows access to the resource if the authenticated user has a role that&#39;s in the set.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;authenticated-base&lt;/code&gt; just checks that the user is authenticated (that the &lt;code&gt;::friend/identity&lt;/code&gt; key is present); no additional authorization comes into play.
&lt;/p&gt;

&lt;p&gt;
The example project performs authentication using an in-memory &quot;database&quot;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;users&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;dummy in-memory user database.&quot;&lt;/span&gt;
  {&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;root&quot;&lt;/span&gt; {&lt;span style=&quot;color: #008b8b;&quot;&gt;:username&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;root&quot;&lt;/span&gt;
           &lt;span style=&quot;color: #008b8b;&quot;&gt;:password&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;creds/hash-bcrypt &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;admin_password&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
           &lt;span style=&quot;color: #008b8b;&quot;&gt;:roles&lt;/span&gt; #{&lt;span style=&quot;color: #008b8b;&quot;&gt;:admin&lt;/span&gt;}}
   &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;jane&quot;&lt;/span&gt; {&lt;span style=&quot;color: #008b8b;&quot;&gt;:username&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;jane&quot;&lt;/span&gt;
           &lt;span style=&quot;color: #008b8b;&quot;&gt;:password&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;creds/hash-bcrypt &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;user_password&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
           &lt;span style=&quot;color: #008b8b;&quot;&gt;:roles&lt;/span&gt; #{&lt;span style=&quot;color: #008b8b;&quot;&gt;:user&lt;/span&gt;}}}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now, let&#39;s define some resources that use these helpers. These resources all use Friend for authorization. They allow, respectively, admins, users and any authenticated user.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;require&lt;/span&gt; &#39;[liberator-friend.resources &lt;span style=&quot;color: #008b8b;&quot;&gt;:as&lt;/span&gt; r &lt;span style=&quot;color: #008b8b;&quot;&gt;:refer&lt;/span&gt; [defresource]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defresource&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;admin-resource&lt;/span&gt;
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:base&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;r/role-auth #{&lt;span style=&quot;color: #008b8b;&quot;&gt;:admin&lt;/span&gt;}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:allowed-methods&lt;/span&gt; [&lt;span style=&quot;color: #008b8b;&quot;&gt;:get&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:available-media-types&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:handle-ok&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Welcome, admin!&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defresource&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;user-resource&lt;/span&gt;
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:base&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;r/role-auth #{&lt;span style=&quot;color: #008b8b;&quot;&gt;:user&lt;/span&gt;}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:allowed-methods&lt;/span&gt; [&lt;span style=&quot;color: #008b8b;&quot;&gt;:get&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:available-media-types&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:handle-ok&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Welcome, user!&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defresource&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;authenticated-resource&lt;/span&gt;
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:base&lt;/span&gt; r/authenticated-base
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:allowed-methods&lt;/span&gt; [&lt;span style=&quot;color: #008b8b;&quot;&gt;:get&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:available-media-types&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;]
  &lt;span style=&quot;color: #008b8b;&quot;&gt;:handle-ok&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Come on in. You&#39;re authenticated.&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now we can serve these out using Compojure:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defroutes&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;site-routes&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;GET&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/&quot;&lt;/span&gt; [] &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Welcome to the liberator-friend demo site!&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;GET&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/admin&quot;&lt;/span&gt; [] admin-resource&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;GET&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/authenticated&quot;&lt;/span&gt; [] authenticated-resource&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;GET&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/user&quot;&lt;/span&gt; [] user-resource&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now let&#39;s hit the shell to test out the custom auth.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-3-2&quot;&gt;Testing with CURL&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-3-2&quot;&gt;
&lt;p&gt;
You can follow along by cloning &lt;a href=&quot;https://github.com/paddleguru/liberator-friend&quot;&gt;the example code&lt;/a&gt; and running &lt;code&gt;lein run&lt;/code&gt; in the project&#39;s root. The default route has no authentication requirement, and returns the string defined in the compojure routes above:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;[sritchie@RitchieMacBook ~]$ curl localhost:8090
Welcome to the liberator-friend demo site!
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now let&#39;s hit the admin resource without basic authentication.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;[sritchie@RitchieMacBook ~]$ curl localhost:8090/admin
Not authorized.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Because we didn&#39;t include a basic auth header, Friend&#39;s &lt;code&gt;basic-auth&lt;/code&gt; middleware returned let the request through without adding &lt;code&gt;::friend/identity&lt;/code&gt;. The request hit the Liberator resource, the &lt;code&gt;:authorized?&lt;/code&gt; check failed, and Liberator delegated to the &lt;code&gt;:handle-unauthorized&lt;/code&gt; decision point defined in &lt;a href=&quot;https://github.com/paddleguru/liberator-friend/blob/master/src/liberator_friend/resources.clj#L218&quot;&gt;friend-resource&lt;/a&gt;. This decision point ONLY throws the Friend exception for &quot;text/html&quot; requests, since I only wanted to redirect for Browser requests. Instead we get the default &quot;Not authorized.&quot; response defined &lt;a href=&quot;https://github.com/paddleguru/liberator-friend/blob/master/src/liberator_friend/resources.clj#L231&quot;&gt;here&lt;/a&gt;, decked out with the proper &lt;code&gt;401 Unauthorized&lt;/code&gt; response code. Thanks, Liberator.
&lt;/p&gt;

&lt;p&gt;
Let&#39;s try it with bad credentials.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;[sritchie@RitchieMacBook ~]$ curl -u root:wrongpass localhost:8090/admin
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
We get no text response, just a &lt;code&gt;401 Unauthorized&lt;/code&gt;. Because I included basic auth credentials and an &lt;code&gt;authorization&lt;/code&gt; header, The &lt;code&gt;basic-auth&lt;/code&gt; workflow in Friend&#39;s middleware DID try to authenticate. When authentication against the &lt;code&gt;users&lt;/code&gt; failed, rather than pass the request through to my liberator &lt;code&gt;:handle-unauthorized&lt;/code&gt; hook, Friend returned its own &lt;a href=&quot;https://github.com/cemerick/friend/blob/master/src/cemerick/friend/workflows.clj#L9&quot;&gt;default response&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
I think that this is the most confusing aspect of integrating Liberator and Friend. Because Friend&#39;s workflows DO sometimes return their own responses, if you&#39;re going to throw an &lt;code&gt;unauthorized!&lt;/code&gt; exception you need to prepare for this and share the proper responses between the middleware resources and your custom workflows.
&lt;/p&gt;

&lt;p&gt;
Finally, with proper credentials:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;[sritchie@RitchieMacBook ~]$ curl -u root:admin_password localhost:8090/admin
Welcome, admin!
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The &lt;code&gt;basic-auth&lt;/code&gt; workflow adds &lt;code&gt;::friend/identity&lt;/code&gt; into the session, &lt;code&gt;:authorized?&lt;/code&gt; checks for the &lt;code&gt;:admin&lt;/code&gt; role and returns true, and &lt;code&gt;:handle-ok&lt;/code&gt; returns &quot;Welcome, admin!&quot;.
&lt;/p&gt;

&lt;p&gt;
What if we supply valid credentials, authenticate properly with Friend, but try to access a route that we&#39;re not authorized to see?
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;[sritchie@RitchieMacBook ~]$ curl -u jane:user_password localhost:8090/user
Welcome, user!

[sritchie@RitchieMacBook ~]$ curl -u jane:user_password localhost:8090/admin
Not authorized.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Friend&#39;s &lt;code&gt;basic-auth&lt;/code&gt; workflow lets both requests through, but &lt;code&gt;:authorized?&lt;/code&gt; returns true in the first case, false in the second. Because Friend&#39;s middleware was happy Friend supplies no response, leaving the response to Liberator. Liberator calls &lt;code&gt;:handle-ok&lt;/code&gt; in the first case and &lt;code&gt;:handle-unauthorized&lt;/code&gt; in the second.
&lt;/p&gt;

&lt;p&gt;
For completeness, here are the same routes with valid admin credentials:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;[sritchie@RitchieMacBook ~]$ curl -u root:admin_password localhost:8090/admin
Welcome, admin!

[sritchie@RitchieMacBook ~]$ curl -u root:admin_password localhost:8090/user
Not authorized.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And proof that the &lt;code&gt;/authenticated&lt;/code&gt; route allows any valid credentials:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;[sritchie@RitchieMacBook ~]$ curl -u root:admin_password localhost:8090/authenticated
Come on in. You&lt;span style=&quot;color: #8b2252;&quot;&gt;&#39;re authenticated.&lt;/span&gt;

&lt;span style=&quot;color: #8b2252;&quot;&gt;[sritchie@RitchieMacBook ~]$ curl -u jane:user_password localhost:8090/authenticated&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;Come on in. You&#39;&lt;/span&gt;re authenticated.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;Conclusions&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
So, there you have it. Friend and Liberator, working in glorious harmony.
&lt;/p&gt;

&lt;p&gt;
As confusing as I find Friend, I think it&#39;s the best solution out there for authentication and authorization for Ring applications. Communication through exception football can be pretty confusing, but it seems like the best way to handle the redirect coordination you need if you want users to be able to &quot;pause&quot; a route, authorize at a different route, then come back to the original URI for another try.
&lt;/p&gt;

&lt;p&gt;
Both of these libraries are worth exploring, and together they sing. After the initial learning curve, the combination has made it easy to iterate on RESTful APIs in Clojure here at &lt;a href=&quot;https://paddleguru.com&quot;&gt;PaddleGuru&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Upcoming Talks in 2013</title>
   <link href="http://sritchie.github.com/2013/08/24/upcoming-talks-in-2013/"/>
   <updated>2013-08-24T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2013/08/24/upcoming-talks-in-2013</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;24 August 2013 - San Francisco&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Upcoming Talks in 2013&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
This is the year I teach myself to become a better public speaker. I&#39;ve spent the past year coding up a number of powerful &lt;a href=&quot;http://sritchie.github.io/projects/&quot;&gt;Scala and Clojure projects&lt;/a&gt;, all the while avoiding the important and difficult work of teaching and writing about the import and use of those projects. Well, no longer.
&lt;/p&gt;

&lt;p&gt;
To mind that gap I&#39;ll be giving a number of talks in 2013 on my recent work on Summingbird and Cascalog. If you&#39;re in the Bay Area, Boston, or Northern VA (my home town!), I&#39;d love to meet you.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;Introduction to Summingbird (Sept 3rd, Bay Area Storm Users)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
&lt;a href=&quot;http://twitter.com/posco&quot;&gt;Oscar Boykin&lt;/a&gt; and I will be giving an Introduction to Summingbird at the Bay Area Storm Users meetup on Tuesday, September 3rd. Summingbird is a streaming MapReduce framework we developed at Twitter that allows you to write a streaming computation once and execute it in batch-mode on Scalding, in realtime mode on Storm, or on both Scalding and Storm in a hybrid batch/realtime mode that has very attractive fault tolerance properties. Sound exciting? RSVP at the event&#39;s &lt;a href=&quot;http://www.meetup.com/Bay-Area-Storm-Users/events/135403842/&quot;&gt;meetup page&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;Realtime MapReduce at Twitter (Sept 22nd, CUFP)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
Check this one out at the &lt;a href=&quot;http://cufp.org/conference/sessions/2013/sam-ritchie-twitter-inc-realtime-mapreduce-twitter&quot;&gt;Commercial Users of Functional Programming&lt;/a&gt; conference, September 22nd-24th in Boston. This talk will focus more on the functional programming ideas we used in the design of Summingbird&#39;s real-time MapReduce DSL and its component libraries (&lt;a href=&quot;https://github.com/twitter/storehaus&quot;&gt;Storehaus&lt;/a&gt;, &lt;a href=&quot;https://github.com/twitter/bijection&quot;&gt;Bijection&lt;/a&gt;, and &lt;a href=&quot;https://github.com/twitter/algebird&quot;&gt;Algebird&lt;/a&gt;). Which ideas helped our design? What hurt?
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;Summingbird, Scala and Storm (Sept 25th, Boston Storm Users)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
Another Introduction to Summingbird talk, with a focus on how Scala&#39;s type system and style helps us write correct Storm topologies. Register at the Boston Storm Users&#39; &lt;a href=&quot;http://www.meetup.com/Boston-Storm-Users/events/135630522/&quot;&gt;meetup page&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;Twitter: Taking Hadoop Realtime with Summingbird (Oct 19th, PNWScala)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
This talk at the &lt;a href=&quot;http://pnwscala.org/&quot;&gt;Pacific Northwest Scala Conference&lt;/a&gt; will get into the guts of Summingbird&#39;s batch/realtime merging abstraction and the streaming MapReduce graph planner we&#39;ve developed. Here&#39;s the abstract:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Twitter&#39;s Summingbird library allows developers and data scientists to build massive streaming MapReduce pipelines without worrying about the usual mess of systems issues that come with realtime systems at scale. This talk will discuss the development of Summingbird&#39;s hybrid Batch and Realtime operating mode, the power of clean, mathematical abstractions and the massive creative leverage that functional design constraints can give to a project.
&lt;/p&gt;

&lt;p&gt;
The talk will also discuss some of the applications for these technologies currently being used at Twitter.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Registration is filling up fast, so &lt;a href=&quot;https://pnwscala2013.eventbrite.com/?ref=elink&quot;&gt;sign up today!&lt;/a&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-5&quot;&gt;Streaming MapReduce in Clojure (Nov 14-16, Clojure/Conj)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-5&quot;&gt;
&lt;p&gt;
This talk will discuss my work on &lt;a href=&quot;https://groups.google.com/forum/#!topic/cascalog-user/F8EkFM7HiE0&quot;&gt;Cascalog 2&lt;/a&gt; and its new &quot;execution platform&quot; abstraction. The recent Cascalog 2 redesign split the datalog compiler apart from the underlying Cascading functions that get called when you run a Cascalog query on a MapReduce cluster. With luck, by the time I deliver the talk, Cascalog will be able to run on &lt;a href=&quot;https://github.com/nathanmarz/storm/wiki/Trident-tutorial&quot;&gt;Trident&lt;/a&gt; and &lt;a href=&quot;https://github.com/mesos/spark&quot;&gt;Spark&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
I&#39;ll be delivering this talk in my hometown of Alexandria, VA at this year&#39;s &lt;a href=&quot;http://clojure-conj.org/&quot;&gt;Clojure/Conj&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-6&quot;&gt;So You Want to Build a MapReduce DSL? (Jan 11th, Data Day Texas, Austin TX)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-6&quot;&gt;
&lt;p&gt;
Still working on the meat of this one, but some initial thoughts - I&#39;d like discuss the vast number of choices in the MapReduce DSL world. Spark, Scoobi, Summingbird, Scalding&amp;#x2026; folks are writing Hadoop DSLs in Ruby, in Mathematica, in Clojure. Where does it end? What&#39;s the right level of abstraction? This talk will discuss why so many different options exist and some pitfalls to beware of when choosing between them.
&lt;/p&gt;

&lt;p&gt;
More information on Data Day Texas, including registration info, on the &lt;a href=&quot;http://datadaytexas.com/&quot;&gt;event page&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Leadville Trail 100</title>
   <link href="http://sritchie.github.com/2013/08/20/leadville-trail-100/"/>
   <updated>2013-08-20T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2013/08/20/leadville-trail-100</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;20 August 2013 - San Francisco&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;The Leadville Trail 100&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
This past weekend I ticked off one of my athletic life goals; the Leadville Trail 100, a brutal 100 mile running race in Colorado. I signed up for Leadville this May after a team injury forced our boat out of the 2013 &lt;a href=&quot;http://www.texaswatersafari.org/&quot;&gt;Texas Water Safari&lt;/a&gt;, a 262 mile canoe race down in Texas that is my usual ultra-length torture for the year. Less than three months is less training than recommended for a hundred miler, but what the hell. Rational thinking never led anyone to a successful hundred mile finish.
&lt;/p&gt;

&lt;p&gt;
Here&#39;s how the trip unfolded.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;The Buildup&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
I arrived in Colorado with parents the Sunday before the race to acclimate. A week living at 9,000&#39; seemed enough. The plan was to code throughout the week, but as nerves took over, work fell by the wayside and I started devouring running books and movies. &lt;a href=&quot;http://www.amazon.com/Once-Runner-John-Parker-Jr/dp/1416597891&quot;&gt;Once a Runner&lt;/a&gt; and &lt;a href=&quot;http://www.amazon.com/Again-Carthage-Novel-John-Parker/dp/B0052HKN5I&quot;&gt;Again to Carthage&lt;/a&gt; by John L. Parker, &lt;a href=&quot;http://www.amazon.com/Eat-Run-Unlikely-Ultramarathon-Greatness/dp/0544002318&quot;&gt;Eat and Run&lt;/a&gt; by Scott Jurek, started a Pre biography&amp;#x2026; I watched &lt;a href=&quot;http://www.ws100film.com/&quot;&gt;Unbreakable&lt;/a&gt; and &lt;a href=&quot;http://vimeo.com/ondemand/inthehighcountry/58457574&quot;&gt;In the High Country&lt;/a&gt; and generally spent my time staring at the mountains and trying to push out &lt;a href=&quot;http://www.inclineclub.com/pics/Anton_krupicka.jpg&quot;&gt;Krupicka-style beard hairs&lt;/a&gt;. This was not successful.
&lt;/p&gt;

&lt;p&gt;
This was my parents&#39; first time crewing an ultra, other than the Texas Water Safari. That race is interesting in that all the support crew needs to provide is water and ice. They need to get to locations along the way, but they don&#39;t necessarily need to be creative or imaginative about WHAT the racer will need along the way. We do that a lot for each other in the boat.
&lt;/p&gt;

&lt;p&gt;
In the days leading up to the race I tried to teach my parents what would be required. We went for a hike up the back side of Hope Pass on Tuesday. I power-hiked, feeling great, and ran down, imagining what a stud I&#39;d be doing the same thing during the race. I even picked out a couple of hundred yard sections that I would run (making up, what, 10 seconds?). How young and optimistic I was. After the hike we drove back to Leadville, bought a course map and visited each of the aid stations. I pretended my brother was racing and tried to conjure up images of the coming race for my parents. &quot;Mike&#39;s coming in, he&#39;s been out for this many hours&amp;#x2026;&quot; and so on. Sort of worked. I made the mistake of calling our way to Tabor boat ramp a &quot;secret way&quot;, which freaked my dad out beyond belief. What other way is there? Are there other racers heading some other way? Jesus, chill out. Don&#39;t do anything different on race day.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://twitter.com/posco&quot;&gt;Oscar Boykin&lt;/a&gt; arrived Wednesday night, ready for action. We did a run the next morning before my girlfriend &lt;a href=&quot;https://twitter.com/jennadawn&quot;&gt;Jenna&lt;/a&gt; showed. I was feeling good and banged out a 7 minute mile before letting Oscar go kill himself at altitude. I felt pretty low on energy that week, but okay with the altitude after a day or two. Strangely lethargic, I slacked on buying food until the day before the race, when I finally forced myself to imagine what I&#39;d need and kicked my ass into gear for a big shopping trip. My parents bought a TON of organizational items (tupperwares! Zip locs! Trash bags!), perhaps not realizing how fast and light a crew needs to be at each of the aid stations. No worries, we&#39;d figure that out later on the road.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;Race Morning&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
The gun goes off for Leadville at 4am. The plan the morning of the race was to wake up at 1am, get about 800 calories in me along with a couple cups of coffee, then hit the sack for a half hour. I got up on time and chowed everything bagel with almond butter and jelly, a greek yogurt and a banana. Chugged one cup of coffee, then back in bed. Another cup of coffee in the car on the way to the race.
&lt;/p&gt;

&lt;p&gt;
My resting heart rate on the way over there was 80 bpm, about 30 beats high. At the start line, it jacked up to 105. Calm down. My plan was to hold back, not go out too fast, and err on the side of SLOW. I figured if I fell back I could always make up the time later. I was shooting for a 24 hour time (foolish on the first Leadville?) And had planned out a pace chart that had me going at the average speed of a 24:32 finisher. If this felt too slow, then all would be well; I&#39;d have energy to start beating the splits in the second half of the race and push the pace down. If too fast, no problem. I&#39;d be in a great position to finish the race.
&lt;/p&gt;

&lt;p&gt;
We pulled into town in the dark and found a spot right near the finish line. I got out, lubed my feet up with BodyGlide, put on my Minimus shoes and ambled over to the coffee shop across the street from the starting line. Some folks were doing warmups back and forth along the road, as if the race wasn&#39;t long and brutal enough to provide that service. So many faces looked haunted in the coffeeshop. I imagine that the excited faces were first timers like me, looking forward to the adventure. Those who had been through the ordeal were wiser and terrified, perhaps remembering some of the pain from past years and questioning their decision to come back to this damned town. Healthy living at 10,200 feet!
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/hOW65ZIq70d/embed/simple&quot; width=&quot;320&quot; height=&quot;320&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
I was trained and ready to go. Oscar, Jenna and the parents would drive the course and meet up with me to provide food and water at various points along the first 50 miles. At mile 50, Andrew Stephens, my partner in the Texas Water Safari and good friend, was to show up and run with me over the mountain. Jenna wanted to be waiting at the top with the Llamas at Hopeless aid station and would take over from Stephens. From there, we&#39;d figure it out on the go.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;Start to May Queen (13.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
I left my folks and Jenna with a kiss and moved out to the start line. The temp was about 40 degrees. I felt calm, but the heart rate was up at 105. The gun went off and I started my watch, shuffling out of the gate at 10:00/mile pace right in the middle of the pack. We all moved down the hill in the cold. Some folks started walking on the first uphill, pacing just right for the distance ahead.
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/hO7h1PvbJlw/embed/simple&quot; width=&quot;320&quot; height=&quot;320&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
The first few miles were surreal. I remembered to look back and take in the sea of headlamps moving down the hill, looked up at the stars and thought about &lt;a href=&quot;http://www.amazon.com/The-Long-Walk-Stephen-King/dp/0451196716&quot;&gt;The Long Walk&lt;/a&gt; by Stephen King. So many hopes and dreams out on the road. It was nice making conversation with people from different towns, starting to make friends then suddenly having folks veer into the woods to pee.
&lt;/p&gt;

&lt;p&gt;
The start of the course rolls down a mile of pavement, transitioning into a long dirt corridor between trees. I focused on the average pace I was trying to maintain - 9:47 minutes per mile - and just stayed slow and shuffled. Ann Trason&#39;s advice, via &lt;a href=&quot;https://twitter.com/eightysteele&quot;&gt;Aaron Steele&lt;/a&gt;, was to be as efficient as possible with every step. I kept my cadence high and started to eat at the half hour point, conserving energy and fueling up.
&lt;/p&gt;

&lt;p&gt;
Casual first five miles, feeling easy. The race was going by faster than I expected, there in the dark. The Peloton hiked up the long scree section at the end of the dirt road, and then it was on, shuffling around Turquoise lake to Tabor boat ramp at mile 7.5.
&lt;/p&gt;

&lt;p&gt;
This is when the first warning signs of later trouble began. I had stopped to pee about 30 minutes into the race. All was clear, which was good. I&#39;d hydrated nicely that morning. Twenty minutes later, I had to pee again. I ended up peeing five times in the first hour and a half. I was drinking fluids and thought I was just on the hydration, but I was peeing more than I drank, which just didn&#39;t make any sense. I later found out that this was a sign of low salt, and that my body would become worse and worse at retaining fluids until I started taking salt pills.
&lt;/p&gt;

&lt;p&gt;
The run around the lake was really beautiful. I was in a SLOW group, walking everything, making about 13 minute miles, so I did try to pass a few people, but was generally happy with the pace and figured that if I lost even 25 minutes here, rested up and then stayed on my pace for the rest of the race, I&#39;d still be within reach of the big buckle. Getting ten minutes up on my pacing this early in the race was going to be a lot harder than taking back ten minutes from the plan near the finish, when most runners spend huge blocks of time walking. Almost took a wrong turn, got back on track, peed again (!), and made it into Tabor where I surprised the parents, Oscar and Jenna with a high five, then back on single track until May Queen.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;May Queen (13.5) to Fish Hatchery (21.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
I pulled into May Queen only eight minutes behind schedule, at around 2:18. Doing so well! Oscar was right there to meet me and handed me my pack. It seemed like a beautiful handoff, until I asked, &quot;Did you add the two scoops of powder?&quot; and Oscar&#39;s eyes got wide.
&lt;/p&gt;

&lt;p&gt;
&quot;I thought you already mixed it!&quot; he said. We were jogging along now and passed Jenna and the family.
&lt;/p&gt;

&lt;p&gt;
&quot;Are there gels in here?&quot;, I said to Jenna.
&lt;/p&gt;

&lt;p&gt;
&quot;No, we thought you added those too!&quot;
&lt;/p&gt;

&lt;p&gt;
I was running along with a pack of straight water and no food. I had skipped the aid station, anticipating a full on-the-move refill, and here I was with nothing. Jenna went back to get me some gels and Oscar took off up to the car to find the carbohydrate powder. I waited at the trail head for seven minutes, knowing how foolish it would be to leave without any food but not wanting to run the half mile back to the aid station. After twelve minutes waiting, it was time to leave. I poured the new pack&#39;s water into my existing pack, saving SOME calories from the remaining perpetuem, and begged a bar off another pace crew. Finally Jenna caught up with me and handed me a couple of gels, but in the confusion I put these in the pack that I left behind&amp;#x2026; so I was off over the first big mountain pass, two hours with a 150 calorie bar when my plan had been 300 calories per hour. It was going to be a long day.
&lt;/p&gt;

&lt;center&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/sritchie&quot;&gt;@sritchie&lt;/a&gt; had a minor crewing difficulty at the last station. Hoping to make up for it at the 22 mile mark. &lt;a href=&quot;http://t.co/yxKtjJDbut&quot;&gt;pic.twitter.com/yxKtjJDbut&lt;/a&gt;&lt;/p&gt;&amp;mdash; P. Oscar Boykin (@posco) &lt;a href=&quot;https://twitter.com/posco/statuses/368727768181116929&quot;&gt;August 17, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
The mountain was fine. I took the steep stuff at a hike, passing people all the way up. I hike well, and the altitude isn&#39;t a big deal at that pace, so I made great time, trying to silence the urge to go fast and make up missing time from the earlier fumble. At this point I was twenty minutes behind pace, a gap that would be easy to catch up. Go slow.
&lt;/p&gt;

&lt;p&gt;
The sun up was beautiful, and I looked around quite a bit, taking it all in. Here I was racing at Leadville! The first tough climb was underway. I started asking folks around me what their pace goals were and seemed to be solidly in a pack of 23-25 hour racers. Was I going out too fast? Or were they all destined to blow up? I chatted with a dude going for his 30th finish (!!) and a girl looking to finish in 23:30 - 11 hours out, 12:30 back. Aggressive pacing goals. We crested the summit and I ran down the powerline, eating up the miles and letting the heart rate drop. The feet and lungs were feeling great. I was hungry, but I was almost to the next aid station, so no problem there.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-5&quot;&gt;Fish Hatchery (21.5) to TreeLine (27.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-5&quot;&gt;
&lt;p&gt;
I saw Jenna and Oscar again when I hit the road. Perhaps embarrassed about the last pacing situation, they presented me with a pack that was just CRAMMED with food. I had only two hours until I would see them again, but they&#39;d given me dozens of items, along with fresh drink mix. I applied sunblock, gave a few handfuls of food pouches back, tried to be nice and moved on out. The clouds were out, which gave the racers a break from the heat. I was 21 miles in and feeling really good, like not much time had passed, but just a bit rough and queasy. I walked the uphills, favoring the achilles a bit, but noticing that I hadn&#39;t really felt any pain at all for quite some time. Other problems had overtaken my achilles issue.
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/hOZgFDpJXAx/embed/simple&quot; width=&quot;320&quot; height=&quot;320&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
A few miles down the road was the Fish Hatchery aid station. I ate a couple of pretzels and choked down a PB&amp;amp;J half, then back out to the road for a long stretch up to treeline. I was dead on the expected pace for the 24 hour finish, maybe pushing a little fast, keeping things in control. Treeline aid was great; I met up with Oscar and the folks, sat down and shook a rock out of my shoe that had been bothering me. Better to take care of these things now than suffer later. They swapped in another pack and some more food and I moved on out into unknown territory.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-6&quot;&gt;TreeLine (27.5) to Twin Lakes (39.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-6&quot;&gt;
&lt;p&gt;
The next section was a long, steady uphill with a big downhill finish into Twin Lakes. I hiked up the initial climb, knowing that I could make up time on the downhill. The goal here was to keep moving and get to Twin Lakes with solid legs in preparation for the grueling trip up Hope Pass.
&lt;/p&gt;

&lt;p&gt;
I started to ask more folks about their time goals. One guy told me that he was going for 25 hours, &quot;like every damned runner within a two mile radius of us&quot;. Fair enough. I ran into a jacked crossfittish guy with his shirt off who seemed to be having trouble with his stomach and gave him some advice on how to eat. Shortly after that, I joked about my swollen fingers to a dude running behind me. &quot;You need salt, man,&quot; he said, and how right he was. I didn&#39;t put this together with my early peeing. Again, an ominous sign of things to come.
&lt;/p&gt;

&lt;p&gt;
The trail was gorgeous, with long uphills and good conversation all the way to the Mt Elbert trailhead. The elevation gain was pretty large here, but the slope was gentle and didn&#39;t feel as terrible as I had expected. By the time we hit the downhill I had taken a couple of salt pills and felt on top of my game. I pushed through the Mt Elbert station and down Twin Lakes. One guy noted that I must have huge balls to have worn Minimus shoes in the run. All my Krupicka-watching and running reading have made a convert of me. I&#39;m not ready to run in Huaraches, but those &lt;a href=&quot;http://hokaoneone-na.com/&quot;&gt;Hoka One Ones&lt;/a&gt; look like a serious departure from the One True Way (sorry Aaron). I cruised downhill at the lead of our little group, MAYBE one guy passing me on the way down. We pulled in to Twin Lakes in 7:18, just three minutes behind the expected pace. Oscar was waiting for me at the top of the hill and led me down to the rest of the family.
&lt;/p&gt;

&lt;center&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/sritchie&quot;&gt;@sritchie&lt;/a&gt; is through 40 miles. Heading up Hope Pass. &lt;a href=&quot;https://twitter.com/search?q=%23LT100&amp;amp;src=hash&quot;&gt;#LT100&lt;/a&gt; &lt;a href=&quot;http://t.co/1d139eRZae&quot;&gt;pic.twitter.com/1d139eRZae&lt;/a&gt;&lt;/p&gt;&amp;mdash; P. Oscar Boykin (@posco) &lt;a href=&quot;https://twitter.com/posco/statuses/368792194741575680&quot;&gt;August 17, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-7&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-7&quot;&gt;Twin Lakes (39.5) to Hope Pass (44.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-7&quot;&gt;
&lt;p&gt;
My coach and two-time Leadville champion Duncan Callahan was waiting at Twin Lakes. I sat for a couple of minutes, taking in food and going over the items in the pack with my parents. I wanted to leave at 7:30. I applied some sunscreen and took my iPhone and headphones from Jenna for the next stretch; the dreaded Hope Pass. Duncan had me eat a couple of pretzels, and I grabbed a PB&amp;amp;J and some boiled potatoes for the trip out. I took my two Excedrin, a salt pill, ate two pepto pills, and chugged a cup of flatted coke.
&lt;/p&gt;

&lt;p&gt;
Mental check-in: at this point I&#39;m feeling epic. It&#39;s mile 40, I&#39;m injury-free and slowly eating away at my already optimistic pace goal. My planned pace up Hope Pass was 22 min/mile; I knew that I could hike the steep at around that pace, and the mile to the base of the climb is perfectly flat. It should be no problem to make up ten more minutes right there.
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/hOKKa7jrKwr/embed/simple&quot; width=&quot;320&quot; height=&quot;320&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
Oscar walked me out of the aid station. Just outside on the trail as I pushed the PB&amp;amp;J down I started feeling queasy. I told Oscar that I might have to puke, matter-of-factly, trying to stay casual. I kept my gut down, but just after he left I went to my knees on the side of the trail and surprised myself with an incredibly forceful vomiting session. I opened my mouth and out poured a stomachful of caffe latte perpetuem, almost no solids in the food, just a liter or so of liquid. Racers along the trail asked me if I was okay. One guy yelled, &quot;Hey, at least you know you&#39;ll feel great when it&#39;s over!&quot; One more time, emptying out the rest of my stomach, and then I was back up, feeling good. I ate another pepto tablet, put my headphones in and kept going. I wanted to start running hard and thank the racer who made the comment. I knew this wasn&#39;t smart and kept the pace back, but knocked out the flat mile to the base of hope in 11 minutes. I thought to myself, &quot;this race isn&#39;t so hard,&quot; and started walking up the hill.
&lt;/p&gt;

&lt;p&gt;
I became more dejected as we rose. The trail on this side of hope was just so steep. I came upon one strong, young racer sitting by the side of the trail, head in hands, moaning to himself. I asked if he was okay. He replied, &quot;yeah, I&#39;m good,&quot; not looking up. Yeah, right. I caught up to a woman who looked really strong and passed her. Ten minutes later I became dizzy and had to sit down. She caught up and offered me a gel, and I decided that passing a bad idea. Time to relax. I was hiking at a 22 min/mile pace, which would land me at the Hopeless aid station exactly on target. My heart rate was up at 168 by now. I had set my heart rate alarm at 165, and after a number of &quot;too high!!&quot; beeps I turned the alarm off. Why was my heart rate so high? Could it be that I was extraordinarily dehydrated and my heart had to work hard to pump my thick blood? Don&#39;t think about it, just shut off the alarm and keep hiking. This is a hard climb. You&#39;re SUPPOSED to work hard.
&lt;/p&gt;

&lt;p&gt;
Half way up I found that I couldn&#39;t quit thinking about water. I knew I had vomited up everything in my stomach and was probably behind on fluid, but I had been very diligent about drinking and didn&#39;t imagine that I was dehydrated. I hadn&#39;t peed in a long time. I was fascinated with the noise of the stream that we were hiking past. A mile more and I actually saw the stream water and dunked my hat, squeezing the cool water on my neck and feeling the amazing restorative effect of cold. I wasn&#39;t out of breath, but I was dizzy from the high heart rate and made myself to stop again. So difficult.
&lt;/p&gt;

&lt;p&gt;
I pushed out of the trees at the 9 hour mark and could see the tents of the aid station up ahead. I knew that I only had a half a mile to go to the top of of Hope Pass; after the summit I could cruise downhill, just as I had on my training run. I&#39;d be at Winfield FAR ahead of schedule. If I stayed smooth I&#39;d get to Winfield at 10:30, leaving me 14:30 to get home. Even feeling diminished, I felt like I had this in the bag. If you ever feel this way on Hope Pass, something is wrong. I had underestimated Leadville.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-8&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-8&quot;&gt;Hope Pass (44.5) to Winfield (50.0)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-8&quot;&gt;
&lt;p&gt;
I headed to the aid station tent, chugged a cup of coke and decided that I would sit down for five minutes. I had puked up the Excedrin earlier, so I grabbed two Tylenol out of a sample dispenser. Ten minutes ahead of pace over a five mile stretch in the hardest part of the race seemed like a foolish way to move. A five minute rest would get me back on track and let me CRANK over the top of the pass.
&lt;/p&gt;

&lt;p&gt;
Two minutes into this break, the nausea hit again and I headed off by a tree just in time to puke up all of the water and calories in my guts yet again. Both heaves were incredibly productive and forced liquid out of my mouth and nose. After the second heave, an aid station medic came over to me and asked how I was doing. I told her I&#39;d let her know in a minute and heaved again, this time coming up with nothing. my stomach was empty, and the two tylenols lay half-dissolved in the grass. The medic made me drink a cup of ramen and asked me how often I had been peeing. I told her that I had peed at Twin Lakes, though thinking back I can&#39;t remember if that&#39;s actually true. Lighting up, I told her that I had puked down there after chugging a cup of coke, just as I had now.
&lt;/p&gt;

&lt;p&gt;
&quot;You don&#39;t have enough salt in your system, honey. You&#39;re trying to put too much into your stomach, and your stomach is shutting down. You need to take smaller bites.&quot; She asked me about my salt intake, then had me lick my wrist and poured a bunch of salt onto it, &quot;just like a tequila shot.&quot; I licked it up and finished my ramen. She brought me another cup of ramen and told me to finish it by the summit. Boom, I was released! I was shivering heavily by then and pulled out my shell, silently congratulating myself on some solid thinking. I would hike slowly to the top and get warm.
&lt;/p&gt;

&lt;p&gt;
As I approached the summit, down came the two guys in the lead, hauling ass down the mountain, not more than a couple of minutes apart. It was fantastic to have almost reached the top before seeing the leaders, and I was grateful for the small rest I got as I moved off of the trail to let them pass.
&lt;/p&gt;

&lt;p&gt;
At the top of the pass down the other side, I started to feel better right away, as expected, and started catching up with the runners in front of me. And suddenly in front of me was a familiar face! Scott Jurek, HAMMERING up the back side of Hope on a hunt for the leaders. I recognized his pacer; Hal Koerner, two-time Western States champion, from my recent viewing of &lt;a href=&quot;http://www.ws100film.com/&quot;&gt;Unbreakable&lt;/a&gt;. I whooped and yelled, standing aside as he came past. Jurek had blue pasties over his nipples.
&lt;/p&gt;

&lt;p&gt;
&quot;Nice pace, man! And nice nipples!&quot; I yelled.
&lt;/p&gt;

&lt;p&gt;
&quot;Thanks,&quot; breathed Scott as he passed. What a warrior.
&lt;/p&gt;

&lt;p&gt;
Inspired, I cruised downhill for about a mile before I remembered that I was hosed and couldn&#39;t keep up that pace. I was starting to sweat and feel terrible inside of my shell. Racers started to pass me for the first time all day. I would step off the trail to let them go, hands on knees, just miserable and thinking about water. The taste of my perpetuem mix was so nauseating. I would take the tiniest sip possible to help me dissolve food, then struggle to keep the mix down. I got down to the treeline without many problems, but just couldn&#39;t run at all and resigned myself to walking the trail. So many incredible runners were passing me on the way up by now. Some of these studs were stopped by the side of the trail, trying to puke as their pacers congratulated us runners coming down.
&lt;/p&gt;

&lt;p&gt;
I thought about the woman at the Hopeless aid station and her question about my pee schedule. When was the last time I had really peed? I decided that I needed to make it happen, and stopped at the side of the trail. I forced it and was surprised and scared to see that it was a dark, dark yellow. Oh boy. I knew that I had been vomiting fluids, but I figured that this was excess and SOME had been getting absorbed. Wrong. I was severely dehydrated, about an hour from aid. Time to get moving.
&lt;/p&gt;

&lt;p&gt;
Finally, there it was, the turn onto the Colorado trail to Winfield. I was walking, but still on pace to get into Winfield at 11 hours. I knew that I would need to stop, but in my mind was thinking, &quot;if I can just get to that station and rest for 10 minutes, I&#39;m going to be okay.&quot; I still thought that I was on target for the Big Buckle. We crossed a stream and I dunked my hat again. Bad idea with the wind. I started shivering, but the cold quelled the pounding in my head and I felt good enough to keep walking and push onward. The desire to sit down by the side of the trail started to set in, but I knew that it wouldn&#39;t help. After a while I could SEE the aid station. My shirtless CrossFit friend caught up. His stomach had started working again, and he was pushing. Many of my old friends from the treeline-&amp;gt;twin lakes section passed me, all trying to cheer me up. &quot;It&#39;s San Francisco!&quot; yelled one lady as she moved by me. Looking great, everyone. One awful mile to go.
&lt;/p&gt;

&lt;p&gt;
Finally, the road. A quarter mile more on the flats and I was at the aid station, 11:05 into the race, RIGHT on track with Duncan&#39;s schedule even with the dehyrdration. Excellent. Just sit down and take ten minutes to get back on pace. Jenna and Oscar found me on the road and led me into Winfield and the checkpoint. I took a moment to smile. I was halfway through the Leadville! Whatever happened from here on out, it was exciting to have gotten through the first 50 miles. I was 17 miles into unknown territory.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-9&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-9&quot;&gt;Winfield (50.0)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-9&quot;&gt;
&lt;p&gt;
The medical staff led me to the weigh-in, and there it was on the scale - 154.5 pounds, down from 165.5 at the start. I had lost 11 pounds of fluid. I sat and Jenna brought me some broth and my fantasy, a cup of straight-up WATER with nothing mixed into it. I drank two cups of water and some broth and started to shiver heavily. This young girl, a doctor, came over and asked me what I wanted to eat. I told her water and oranges, and she brought me these and gave me a sleeping bag to wrap around my legs. My lips were very blue. I thought that my parents might ask me if I wanted to quit, but I knew that I wouldn&#39;t. I also knew that in my current state I wouldn&#39;t be able to continue. How to resolve the paradox?
&lt;/p&gt;

&lt;p&gt;
This is where the race really surprised me. I had done everything right, I thought. I was eating and drinking, pacing myself really well, shielding my eyes and skin from the sun&amp;#x2026; but here this doctor was, telling me that my stomach had shut down and that my kidneys might be failing. I was extremely low on salt and wasn&#39;t retaining water, and they wouldn&#39;t let me go until I could pee again. What the hell? Renal failure? How are you supposed to tough out a race when the machine, your body, betrays you? Was my body just not build to run ultras? After three Texas Water Safari wins, this was hard to believe. Still, I didn&#39;t know how to recover.
&lt;/p&gt;

&lt;p&gt;
At this point, I must have looked seriously fucked up because Oscar wouldn&#39;t even take a video of me. Here&#39;s a Vine he took after forty minutes:
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/hObBXHgQXAQ/embed/simple&quot; width=&quot;320&quot; height=&quot;320&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
As I was suffering, like a dream, Andrew Stephens showed up, ready to RACE. He had his pack on and immediately took command of the situation. He made me change into a warm, dry shirt, put a hat on me and gave me two salt pills. He looked mildly annoyed when I jumped out my chair, ran behind the tent and puked up everything in my stomach. I sat back down inside and he started feeding me water and more salt pills, then asked the doctor if they had any Zofran. They did, and I took one. Zofran is an anti-nausea medication for chemo patients. I knew that if this didn&#39;t settle my stomach, I was hosed.
&lt;/p&gt;

&lt;p&gt;
But it worked! I was able to hold down two liters of water and four more salt pills. I warmed up and began to feel better. I was swaddled in blankets at this point, an hour and ten minutes into my rest at Winfield. I couldn&#39;t pee yet, but I was drinking water with an electrolyte supplement. M&amp;amp;Ms tasted good, as did broth and small sips of coke. I started sipping on an Ensure as well, keeping it all down.
&lt;/p&gt;

&lt;p&gt;
And then, an hour and a half into the rest, I had to pee! I went out to the porta-potty and forced out a sample. It was dark, but not AS dark. I had gained back three pounds now. I showed another doctor and he smiled. &quot;Your kidneys aren&#39;t failing, you&#39;re just severely dehydrated. Start running!&quot; My pulse ox was back up to 97% (from 86%). Once Stephens heard that, it was go time. I felt like Jurek now, MUCH better, and wanted to hammer. My motivation was back. I had lost sight of the 25 hour belt buckle, but I thought now that we&#39;d be able to finish this race in a respectable time.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-10&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-10&quot;&gt;Winfield (50.0) to Twin Lakes (59.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-10&quot;&gt;
&lt;p&gt;
So there it was, out of the aid station 12:45 into the race, 1:15 ahead of the cut-off time. I wanted to run, but Stephens warned me that this was a bad idea. We were already passing dozens of runners on the trail. His new rule was that every time I wanted to start running, I needed to drink. He started feeding me pieces of bagel and cream cheese, which I washed down with the water. &quot;If you go slow, you&#39;re going to finish this thing. If you start puking, it&#39;s all over.&quot; I knew he was right and made me choice. We went slow.
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/hObB12Hv72n/embed/simple&quot; width=&quot;320&quot; height=&quot;320&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;
At the turn to the Hope Pass climb Andrew said, &quot;this is the most badass race of all TIME. I&#39;m definitely coming back to do this next year.&quot; He was obsessed already, like me during my first Texas Water Safari. I was mildly pleased to hear him gasping as we pushed it up toward 11,000 feet. I tried to keep my breathing under control to seem like a badass, then gave in and huffed and puffed.
&lt;/p&gt;

&lt;p&gt;
Once we hit the switchbacks, Andrew said, &quot;Just two more to go and then we&#39;re at the summit.&quot;
&lt;/p&gt;

&lt;p&gt;
&quot;Dude,&quot; I said, &quot;we have 1.2 miles to go. I just ran the course.&quot;
&lt;/p&gt;

&lt;p&gt;
&quot;I know that. I was just trying to trick you and make you feel better,&quot; he said. Nice. Runners around us started pitching in with stories of their pacers trying to trick them too. We all death marched up to the summit at 12,400, taking the time to look at the gorgeous scenery and enjoy the day.
&lt;/p&gt;

&lt;p&gt;
I had come back from the dead, and I KNEW that I was going to be able to finish the race. Now that I was hydrated my heart rate had dropped to 140, down from 168 of the first trip over Hope. At the summit we started running downhill to the aid station. The station had run out of cups, but Andrew figured out some system and got me another cup of broth. We took five minutes, I cleaned out my shoes and made Andrew take a picture of me by the llamas so we could show Jenna what she was missing. Seven minutes later, now 1:45 ahead of the cutoff, we took off down the hill, passing people and comparing Leadville to the Texas Water Safari. I kept eating and drinking, feeling better and better with every mile.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-11&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-11&quot;&gt;Twin Lakes (59.5) to TreeLine (71.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-11&quot;&gt;
&lt;p&gt;
We pulled in to Twin Lakes far enough ahead of the cutoff that I stopped thinking about it, and Oscar switched in on the pacing duties for Andrew. After the morning pacing situation, I was overly worried about having a pacer other than Andrew, and was a little mean and intense toward Oscar. I tore into him right away at the Twin Lakes aid station with rapid fire questioning. &quot;What do you have in your pack? Where&#39;s your headlamp? Where are the extra batteries?&quot; Just chewing him out like he was a kid. I&#39;m sure he wanted to punch me, and would have if he didn&#39;t have his own killer background in long-distance running and an understanding of the strange mental states that one can experience during these races. My big concern was that I would hit the wall and need my pacer to take complete control. Only later did I realize the incongruity of yelling, &quot;Are you going to tell me what to do!!?&quot;.
&lt;/p&gt;

&lt;p&gt;
It was just getting dark. I changed my shoes, hit the bathroom (thank god, organs functioning again!) and Oscar and I took off up the hill. It was time to hike again using the Stephens method, keeping the heart rate under control and eating as much as I could without getting sick.
&lt;/p&gt;

&lt;p&gt;
At this point I was feeling great again. I though I understood what had gone wrong, and knew that if I could keep eating and drinking and stay ahead on fluids and salt that I would be able to finish this race. I hadn&#39;t known how important salt was, but I damned well understood it now, and felt that the potential problems of the race were all under my control. I wasn&#39;t affected by the altitude, and had excellent supplies and aid stations the whole way home. I was going to finish this race.
&lt;/p&gt;

&lt;p&gt;
Excellent pacing by Oscar through the whole section. We came to the Mt Elbert aid station, fluids only. I rolled through while Oscar filled up a bottle then caught up to me, cruising along in his Merrell trail gloves. We were hiking the uphills and running every flat and downhill, passing people like crazy and feeling great about the beautiful, warm night.
&lt;/p&gt;

&lt;p&gt;
In no time, we were through the long stretch and walking the last mile or two into the Half Pipe aid station. I drank a delicious cup of hot chocolate and some broth, and we carried potato chips and other items out along the road. As before, we spent very little time in the aid station. A couple of miles later we hit the TreeLine, mile 71.5. The parents, Jenna and Stephens were waiting for me with an Andrew Stephens special: the Leadville pizza. Salami and cheddar cheese on top of a bagel. (Where had the cheddar come from?)
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-12&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-12&quot;&gt;TreeLine (71.5) to PowerLine (79.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-12&quot;&gt;
&lt;p&gt;
As happens in the Safari, at this point I had the feeling that the race was almost finished. This was ridiculous, as the farthest I had run before the day in question was around 33 miles, only a short 5k farther than the distance we had yet to go. Oscar and I started talking about code, running for a while, walking for a while and the miles melted away. A woman along the road heard us talking about Scala and Hadoop and called us a &quot;couple of trail philosophers!&quot; I was surprised that we were so upbeat, given the day&#39;s events. The night was incredible, stars just blanketing the sky above. So beautiful. In a few miles, we&#39;d reach the base of powerline and Jenna would pick up crewing duties. Another cup of hot chocolate in the Fish Hatchery aid station, a water top-off and we were off.
&lt;/p&gt;

&lt;p&gt;
Just as I was pouring my unwanted hot chocolate out on the road, I heard Oscar say, &quot;Can I get a sip of that?&quot; But it was too late. Only a drop remained, but it seemed enough to cheer him up and power him through the remaining mile. We cruised the final downhill to Jenna, all decked out in pacing attire and a fresh headlamp. My spirits were getting higher and higher.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-13&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-13&quot;&gt;PowerLine (79.5) to May Queen (86.5)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-13&quot;&gt;
&lt;p&gt;
Not much to report for this section, other than fantastic pacing from Jenna. I was sticking to the Stephens formula now, drinking, looking at my watch and eating every twenty minutes, trying for 250 calories per hour and keeping my heart rate under 145. I was happy to be out and healthy around Jenna, but I took time to prepare her for the monster that would reveal itself if I did crash, and how she&#39;d have to force me to eat and drink as I was crying and puking at her feet. Luckily the nightmare didn&#39;t come to pass. We passed a bunch of people and ran the entire downhill, all the way through the two miles of single track, over the bridges and out to the road. An older couple was waiting at the exit of the single track.
&lt;/p&gt;

&lt;p&gt;
&quot;Go runners, you look great!&quot; they yelled. Then, &quot;Hey, is that Sam?&quot; It was my parents, huddled in the cold with a table and a little tray of various treats. Jenna and I ran up, grabbed a few items of food and a packet of Chamois Butter out of the box and kept on cruising. We were both feeling great and Jenna decided to crew me for five more miles. 15 miles of pacing is no joke, but she maintained her stoke the entire time, hauling water and food for me, fetching and mixing up various treats and medicinal concoctions as I requested them.
&lt;/p&gt;

&lt;p&gt;
The May Queen aid station was just intense. The runners looked like ravaged mine workers, slumped, dejected, sipping away at cold broth and lukewarm cups of coffee. I sat down to relube my feet while Jenna ran to grab extra food. We greeted the runners who were present (including the old Italian man we&#39;d met in town before the race, the one running the Spartathlon in a couple of weeks!) then got up and hauled ass out of the gate. We weren&#39;t going to break any records, but I still wanted to come into the finish solidly under 27 hours. I had rested at the 50 mile point for 1:40. My goal was to finish 1:40 slower than my intended target time of 24:35. We left MayQueen with this in mind, solidly on track, sipping Ensure and pushing for the boat ramp.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-14&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-14&quot;&gt;May Queen (86.5) to Leadville (100.0)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-14&quot;&gt;
&lt;p&gt;
I started to feel quite tired along this stretch and lost my discipline, running the uphills on the single track around the lake. I wanted to reach town before sunrise. Jenna saw what was happening and forced me to slow down and eat. I knew that Stephens would have been proud. We still had 11 miles to go, probably 2.5 hours on fatigued legs. We were passing racers and pacers, and there was no need to do anything special. Now that it was night, just as it had been on the way out, I remembered the course well and applied myself to the task at hand. Eat, drink, and shuffle along the miles.
&lt;/p&gt;

&lt;p&gt;
At 7.8 miles to go we hit Tabor boat ramp and there was Oscar, packed up and right on schedule. He switched in for Jenna and we kept on at the same pace, passing runners, talking less than before as I dealt with the exhaustion. My ankles were hurting quite a bit. Get to the downhill scree, then it&#39;s smooth sailing to the finish, I thought.
&lt;/p&gt;

&lt;p&gt;
Finally, we were away from the lake. We crossed a road, following a line of glow sticks over to a steep, sandy, rock-covered slope, about a quarter mile long. At the bottom, finally, blessedly&amp;#x2026; flat road, and the five mile mark.
&lt;/p&gt;

&lt;p&gt;
Now, at this point the distance to go became comprehensible. I knew that five miles back home was about the distance from the golden gate bridge to my apartment. I&#39;d run this route often in training, no problem. I knew what pace I could hold. We could see the barest glow of sunlight over the town, dimming the stars out above the mountains. I forced the pace down to an 11 minute mile, fast enough that Oscar could stop pretending to jog and actually move his feet a little. Gotta give the old man a workout. The last five miles wasn&#39;t marked and no one was out to cheer for us, so it was a quiet push. I ate a peanut butter cracker every ten minutes and tried to keep the pace.
&lt;/p&gt;

&lt;p&gt;
Right turn at the rail road tracks. Down the road, by the trailers, left turn up the rocky slope, passing runners&amp;#x2026; and there it was, 3 miles to go, the long dirt road lined with trees where a long day earlier I&#39;d had conversations with new friends, all happy, all full of hope. The road sloped gently uphill and we started to push faster and faster, only 2.5 miles to go now, 1.5 miles to the road. A while later we could see it, and we started to run faster and faster. All of the pain was gone from my legs. I couldn&#39;t feel my ankles, I couldn&#39;t feel my lungs, and my heart rate stayed low, unaffected by the altitude. I clicked the pace higher as we hit the final incline to the pavement, checking my watch. We&#39;d dropped our average from 11:00 to 9:30 per mile.
&lt;/p&gt;

&lt;p&gt;
&quot;How fast are we going?&quot; asked Oscar, pushing uphill next to me.
&lt;/p&gt;

&lt;p&gt;
&quot;Must be eight minutes,&quot; I said. I ran harder and joked, &quot;What&#39;s wrong, too fast for you?&quot; Oscar was running a marathon in two weeks, and had already jammed 26 miles. He said, &quot;It is, man. Go for it, I&#39;ll watch you run,&quot; and slowed down, letting me pull ahead.
&lt;/p&gt;

&lt;p&gt;
This was it. The final mile. I was feeling fantastic and pushed faster and faster, past racers, up over the hill by the middle school to the final stretch&amp;#x2026; and there it was, the finish line, with 26:12 on the watch and just a short distance to go. I was going to make it! I was down at a 6:30 now and hit the downhill flying, feeling a little ridiculous moving past limping runners trying to drag themselves in. Who would race for such an arbitrary time goal? 26:15?
&lt;/p&gt;

&lt;p&gt;
Up the hill with Jenna, arms pumping, down the red carpet and across the line, finally, finally finished in 26:15:12. I hugged the race director, who hung my finisher&#39;s medal around my neck and sent me over to to medical. I stepped on the scale and weighed in at 163.5, 9 pounds up from my diminished state at mile 50.
&lt;/p&gt;

&lt;p&gt;
I walked over to my parents, Jenna and Oscar, feeling fantastic and joking about taking another lap around the building. I felt so good! It was eerie, walking around not even tired. I sat down for a minute to take my shoes off, and when I tried to get up&amp;#x2026;. boom, it hit me. I suddenly could barely stand. My body locked up and I started shivering again. But it was all over. We&#39;d done it, and I was happy.
&lt;/p&gt;

&lt;center&gt;
&lt;iframe class=&quot;vine-embed&quot; src=&quot;https://vine.co/v/hOVPKmE7rV7/embed/simple&quot; width=&quot;320&quot; height=&quot;320&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;script async src=&quot;//platform.vine.co/static/scripts/embed.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Cascalog Testing 2.0</title>
   <link href="http://sritchie.github.com/2012/01/22/cascalog-testing-20/"/>
   <updated>2012-01-22T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2012/01/22/cascalog-testing-20</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;22 Jan 2012 - San Francisco&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Cascalog Testing 2.0&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
A few months ago I announced &lt;a href=&quot;http://sritchie.github.com/2011/09/30/testing-cascalog-with-midje.html&quot;&gt;Midje-Cascalog&lt;/a&gt;, my layer of Midje testing macros over the &lt;i&gt;Cascalog&lt;/i&gt; MapReduce DSL. These allow you to write tests for your Cascalog jobs in a style that mimics Cascalog&#39;s own query execution syntax. In this post I discuss midje-cascalog&#39;s 0.4.0 release, which brings tighter Midje integration and a number of new ways to write tests. I&#39;ll start with a refresher on the old syntax before debuting the new. If you&#39;re eager, add the following to your project.clj:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;[midje-cascalog &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;0.4.0&quot;&lt;/span&gt;]
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;Midje-Cascalog Refresher&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
Take the following Cascalog query:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;use&lt;/span&gt; &#39;cascalog.api&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;word&quot;&lt;/span&gt;]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;?&amp;lt;- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;stdout&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
       [?out-word]
       &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?word&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
       &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;str&lt;/span&gt; ?word &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot; up!&quot;&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?out-word&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Executing this code at the repl prints a single tuple with the string &lt;code&gt;word up!&lt;/code&gt; to standard out.
&lt;/p&gt;

&lt;p&gt;
How would you go about testing that this is true? With midje-cascalog, you would swap out the &lt;code&gt;?&amp;lt;-&lt;/code&gt; form for its testing equivalent: &lt;code&gt;fact?&amp;lt;-&lt;/code&gt;. Here&#39;s the same Cascalog test alongside a typical Midje test:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;word&quot;&lt;/span&gt;]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?&amp;lt;- [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;word up!&quot;&lt;/span&gt;]]
           [?out-word]
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?word&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;str&lt;/span&gt; ?word &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot; up!&quot;&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?out-word&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;+ should add two numbers.&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;+&lt;/span&gt; 2 2&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; 4&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
I find that &lt;code&gt;fact?&amp;lt;-&lt;/code&gt; and &lt;code&gt;fact?-&lt;/code&gt; macros can be a bit confusing when you start mixing Cascalog and Midje tests, as they break the Midje pattern of &lt;code&gt;&amp;lt;thing-to-test&amp;gt; =&amp;gt; &amp;lt;expected-thing&amp;gt;&lt;/code&gt;. The syntax updates fix all of this with a set of checker functions that mimic Midje&#39;s excellent &lt;a href=&quot;https://github.com/marick/Midje/wiki/Checkers-for-collections-and-strings&quot;&gt;set of collection checkers&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;The &quot;produces&quot; checker&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
Midje-cascalog 0.4.0 introduces the &lt;code&gt;produces&lt;/code&gt; function, mirroring Midje&#39;s &lt;code&gt;just&lt;/code&gt;. Let&#39;s define a source of tuples and a query to test.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;use&lt;/span&gt; &#39;cascalog.api&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;require&lt;/span&gt; &#39;[cascalog.ops &lt;span style=&quot;color: #008b8b;&quot;&gt;:as&lt;/span&gt; c]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;src&lt;/span&gt;
  [[1 2] [1 3]
   [3 4] [3 6]
   [5 2] [5 9]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;adds the values in each input tuple, sorts the output and returns&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;2-tuples of the first number and the sum. [1 2] becomes [1 3], for&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;example.&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;query&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?x ?sum]
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?x ?y&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:sort&lt;/span&gt; ?x&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;c/sum ?y &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?sum&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
You can think of a query as a set of tuples waiting to be generated (through query execution). With Midje, you test sets using the &lt;code&gt;just&lt;/code&gt; checker:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
  [1 2 3] =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;just [1 2 3]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;    &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  [1 2 3] =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;just [1 2 3 4]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;false&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The cascalog analog to &lt;code&gt;just&lt;/code&gt; is the &lt;code&gt;produces&lt;/code&gt; checker. &lt;code&gt;produces&lt;/code&gt; works like &lt;code&gt;just&lt;/code&gt;, but against queries instead of bare collections. Executing the following test shows that the query produces the expected set of pairs, in any order:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces [[3 10] [1 5] [5 11]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;  &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces [[1 5] [3 10] [5 11]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
You can read this test as saying &quot;query, when executed, produces [3 10], [1 5] and [5 11]. You can also check that a query &lt;b&gt;doesn&#39;t&lt;/b&gt; produce a set of tuples by swapping out =not=&amp;gt; for =&amp;gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact
  query =not=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;string!&quot;&lt;/span&gt; 11] [1 5] [5 11]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Using the &lt;code&gt;:in-order&lt;/code&gt; keyword after the expected tuple sequence forces the test to respect ordering:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
  query =not=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces [[3 10] [5 11] [1 5]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-order&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces [[1 5] [3 10] [5 11]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-order&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;    &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
(&lt;code&gt;:in-order&lt;/code&gt; is really only helpful in cases where output is sorted, like our query above.)
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;produces-some&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
The &lt;code&gt;produces-some&lt;/code&gt; checker tests that a query&#39;s output contains a subset of tuples:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-some [[5 11] [1 5]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Note that the behaviour of &lt;code&gt;produces-some&lt;/code&gt; is similar to the behavior of Midje&#39;s &lt;code&gt;contains&lt;/code&gt; collection checker.
&lt;/p&gt;

&lt;p&gt;
As with produces, you can use the &lt;code&gt;:in-order&lt;/code&gt; keyword to force &lt;code&gt;produces-some&lt;/code&gt; to respect ordering. Gaps between tuples are okay.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
  query =not=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-some [[5 11] [1 5]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-order&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-some [[1 5] [5 11]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-order&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;    &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Adding the &lt;code&gt;:no-gaps&lt;/code&gt; keyword introduces the constraint that tuples must also be contiguous:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
  query =not=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-some [[1 5] [5 11]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-order&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:no-gaps&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-some [[1 5] [3 10]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-order&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:no-gaps&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;    &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;produces-prefix and produces-suffix&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
&lt;code&gt;produce-prefix&lt;/code&gt; mimics the &lt;code&gt;has-prefix&lt;/code&gt; collection checker by checking that some set of tuples is produced at the beginning of the query&#39;s output. &lt;code&gt;produces-prefix&lt;/code&gt; implicitly assumes that tuples will be produced in order with no gaps:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-prefix [[1 5]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;         &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-prefix [[1 5] [3 10]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Similarly, &lt;code&gt;produce-suffix&lt;/code&gt; mimics the &lt;code&gt;has-suffix&lt;/code&gt; collection checker by checking that the supplied set of tuples is produced at the tail end of a query:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-suffix [[5 11]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-5&quot;&gt;log-level keywords&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-5&quot;&gt;
&lt;p&gt;
In addition to the keyword options supported above, every one of these checkers supports on optional logging-level keyword. For example, the following two facts are equivalent, but the second one produces &lt;code&gt;:info&lt;/code&gt; level logging when it runs:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-suffix [[5 11]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;        &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  query =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-suffix [[5 11]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:info&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Log level keywords can be useful when debugging tests, as errors will often only appear in the logging output. Currently supported keywords are &lt;code&gt;:off&lt;/code&gt; (the default), &lt;code&gt;:fatal&lt;/code&gt;, &lt;code&gt;:warn&lt;/code&gt;, &lt;code&gt;:info&lt;/code&gt; and &lt;code&gt;:debug&lt;/code&gt;. The log level needs to be the first keyword argument if you supply multiple.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-6&quot;&gt;wrap-checker&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-6&quot;&gt;
&lt;p&gt;
The real power of the &lt;code&gt;0.4.0&lt;/code&gt; update is the way in which the previous query checkers were defined. Each of the above checkers mimics the behavior of one of Midje&#39;s built-in collection checkers with slightly different keyword arguments. This makes sense if you think of a query as a collection of tuples waiting to be produced (by query execution). The above checkers will get you quite a ways, but what if you want to test a query against some other Midje collection checker?
&lt;/p&gt;

&lt;p&gt;
The answer is &lt;code&gt;wrap-checker&lt;/code&gt;. &lt;code&gt;wrap-checker&lt;/code&gt; is a higher-order function that accepts a midje collection checker and wraps it up, turning it into a Cascalog query checker. I&#39;ll demonstrate the power of this function by wrapping  Midje&#39;s &lt;code&gt;has&lt;/code&gt; checker.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;has&lt;/code&gt; is a powerful way to run functions across every value in some sequence:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact
  [1 3 5 7 9] =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;has every? odd?&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  [1 3 5 6] =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;has some even?&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;   &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
If you try to use &lt;code&gt;has&lt;/code&gt; against a query it will fail, as it expects to be tested against a sequence, not an unexecuted query. Here&#39;s how to get around this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;odd-tuple?&lt;/span&gt; [tuple]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;odd?&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;first&lt;/span&gt; tuple&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;even-tuple?&lt;/span&gt; [tuple]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;even?&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;first&lt;/span&gt; tuple&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;has-tuples&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wrap-checker has&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;new-query&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src [[1] [3] [5]]]
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?x] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?x&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact
  new-query     =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;has-tuples every? odd-tuple?&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
  new-query =not=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;has-tuples some even-tuple?&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;has-tuples&lt;/code&gt; will support log-level keywords like any of the predefined query collection checkers.
&lt;/p&gt;

&lt;p&gt;
A few more examples:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;id-query&lt;/span&gt; [src]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?x] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?x&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [one-of-tuples &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wrap-checker one-of&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      two-of-tuples &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wrap-checker two-of&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      src [[1] [3] [4]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;facts
    src            =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;two-of odd-tuple?&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;           &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
    src            =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;one-of even-tuple?&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;          &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;id-query src&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;two-of-tuples odd-tuple?&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;    &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;id-query src&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;one-of-tuples even-tuple?&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-7&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-7&quot;&gt;Backwards Compatibility&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-7&quot;&gt;
&lt;p&gt;
All of the collection checkers discussed above can be used with the &lt;code&gt;fact?&amp;lt;-&lt;/code&gt; and &lt;code&gt;fact?-&lt;/code&gt; macros:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?&amp;lt;- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;produces-some [[1 5] [5 11]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-order&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
         [?x ?sum]
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?x ?y&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:sort&lt;/span&gt; ?x&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;c/sum ?y &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?sum&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;fact?&amp;lt;-&lt;/code&gt; and &lt;code&gt;fact?-&lt;/code&gt; are also compatible with all of Midje&#39;s unwrapped collection checkers, as discussed &lt;a href=&quot;http://sritchie.github.com/2011/09/30/testing-cascalog-with-midje.html&quot;&gt;here&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-8&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-8&quot;&gt;Conclusion&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-8&quot;&gt;
&lt;p&gt;
Midje is an astonishingly good testing framework; I&#39;m continually surprised by how well its idioms and conventions satisfy Cascalog&#39;s needs. In my next post here I&#39;ll go over some of the more subtle details of the &lt;code&gt;wrap-checker&lt;/code&gt; function. For the curious, &lt;a href=&quot;https://github.com/sritchie/midje-cascalog/blob/develop/src/midje/cascalog.clj#L39&quot;&gt;here&#39;s the code&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
If you&#39;d like more information or additional features, please add your thoughts to the &lt;a href=&quot;https://github.com/sritchie/midje-cascalog/issues&quot;&gt;midje-cascalog github issues page&lt;/a&gt;, or let me know in the comments below (or on twitter! I&#39;m &lt;i&gt;@sritchie09&lt;/i&gt;.)
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Introducing Cascalog-Contrib</title>
   <link href="http://sritchie.github.com/2011/11/15/introducing-cascalogcontrib/"/>
   <updated>2011-11-15T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2011/11/15/introducing-cascalogcontrib</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;Nov 15 2011 - Washington DC&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Cascalog-Contrib&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
I&#39;ve had the pleasure of working with &lt;a href=&quot;https://github.com/nathanmarz/cascalog&quot;&gt;Cascalog&lt;/a&gt; for about ten months now, and have seen the community produce some fantastic work. A &lt;a href=&quot;https://www.assembla.com/spaces/cascalog/wiki/Who&#39;s_using_Cascalog&quot;&gt;number of businesses&lt;/a&gt; are using Cascalog in production; I use Cascalog at Twitter every day to write MapReduce queries for the new &lt;a href=&quot;http://techcrunch.com/2011/09/13/twitter-analytics/&quot;&gt;Twitter Web Analytics&lt;/a&gt; product.
&lt;/p&gt;

&lt;p&gt;
One thing Cascalog doesn&#39;t yet have is a community repository for generic queries and operations. To fill this gap we&#39;ve created &lt;a href=&quot;https://github.com/nathanmarz/cascalog-contrib&quot;&gt;cascalog-contrib&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Cascalog-contrib will be home to any higher-level abstractions over Cascalog that the community is willing to submit. If you have an idea for a module, file a pull request on GitHub or bring it up on the &lt;a href=&quot;http://groups.google.com/group/cascalog-user&quot;&gt;mailing list&lt;/a&gt; for discussion.
&lt;/p&gt;

&lt;p&gt;
The first cascalog-contrib modules are now live on &lt;a href=&quot;http://clojars.org/cascalog-contrib&quot;&gt;clojars&lt;/a&gt;. To include them in your leiningen or cake project, add any of the following to &lt;code&gt;project.clj&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;[cascalog-checkpoint &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;0.1.1&quot;&lt;/span&gt;]
[cascalog-incanter &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;0.1.0&quot;&lt;/span&gt;]
[cascalog-math &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;0.1.0&quot;&lt;/span&gt;]
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Contrib currently has three modules: &lt;code&gt;cascalog.math&lt;/code&gt;, &lt;code&gt;cascalog.incanter&lt;/code&gt; and &lt;code&gt;cascalog.checkpoint&lt;/code&gt;. &lt;code&gt;math&lt;/code&gt; and &lt;code&gt;incanter&lt;/code&gt; are still fairly sparse, but &lt;code&gt;checkpoint&lt;/code&gt; is quite interesting and battle-tested at Twitter. Read on if you&#39;re interested in the details of the &lt;code&gt;checkpoint&lt;/code&gt; module; otherwise, I&#39;ll see you on the mailing list!
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;cascalog.contrib.checkpoint&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
The &lt;code&gt;workflow&lt;/code&gt; macro in the checkpoint module allows you to break complicated workflows out into small, checkpointed steps. If one of these steps causes a job to fail and you restart the job, the workflow macro will skip every step up to the previous point of failure. Fault-tolerant MapReduce topologies ftw!
&lt;/p&gt;

&lt;p&gt;
Let&#39;s look at the workflow macro in action. The following function takes an input-path to some existing Twitter data and an output-path, and executes a tweet-processing workflow with five steps:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;-main&lt;/span&gt;
  [input-path output-path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;workflow [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/tmp/example-checkpoint&quot;&lt;/span&gt;]
            step-1     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; [staging-path]]
                          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;transfer-tweets input-path staging-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

            step-2     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:last&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; user-path]
                          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;harvest-users staging-path user-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

            step-3a    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; step-2 &lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; [cluster-path friend-path]]
                          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;cluster-users user-path cluster-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;count-friends user-path friend-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

            step-3b    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; step-2 &lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; age-path]
                          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;examine-ages user-path age-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

            final-step &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:all&lt;/span&gt;]
                          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;big-analysis cluster-path
                                        friend-path
                                        age-path
                                        output-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Let&#39;s look at this one piece at a time. The first argument to &lt;code&gt;workflow&lt;/code&gt; is a vector with some path that the workflow can use to stage temporary files.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;workflow [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/tmp/example-checkpoint&quot;&lt;/span&gt;] ...&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
It doesn&#39;t matter what path you choose; just make sure that Hadoop has access and can write data to the folder.
&lt;/p&gt;

&lt;p&gt;
Following this vector, &lt;code&gt;workflow&lt;/code&gt; expects pairs of the form
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;step-name &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; &amp;lt;optional-deps, defaults to &lt;span style=&quot;color: #008b8b;&quot;&gt;:last&amp;gt;&lt;/span&gt;]
             &lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; [&amp;lt;optional, creates temp-dirs if supplied&amp;gt;]
             ...&amp;lt;body, same as inside let&amp;gt;...&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Steps can identify other steps as dependencies by referencing their step-names with the &lt;code&gt;:deps&lt;/code&gt; keyword argument.
&lt;/p&gt;

&lt;p&gt;
The first step creates a temporary directory by supplying the symbol &lt;code&gt;staging-path&lt;/code&gt; to the &lt;code&gt;:tmp-dirs&lt;/code&gt; keyword argument. It then transfers tweets from the input directory into this staging directory, where they will remain available for future steps to consume.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;step-1 &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; [staging-path]]
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;transfer-tweets input-path staging-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Step 2 marks &lt;code&gt;:last&lt;/code&gt; as a dependency. &lt;code&gt;:last&lt;/code&gt; is the default, and marks the step as dependent only on the step directly above. A step will not execute until all of its dependencies have completed successfully.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;step-2&lt;/code&gt; uses &lt;code&gt;staging-path&lt;/code&gt; defined in &lt;code&gt;step-1&lt;/code&gt; and creates a new temp directory (&lt;code&gt;user-path&lt;/code&gt;) for its results.
&lt;/p&gt;

&lt;p&gt;
If &lt;code&gt;step-2&lt;/code&gt; fails for any reason and you restart the workflow, the workflow macro will skip &lt;code&gt;step-1&lt;/code&gt;, destroy any temporary directories created in the previous run of &lt;code&gt;step-2&lt;/code&gt;, and start &lt;code&gt;step-2&lt;/code&gt; afresh.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;step-2 &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:last&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; user-path]
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;harvest-users staging-path user-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The next two steps, &lt;code&gt;step-3a&lt;/code&gt; and &lt;code&gt;step-3b&lt;/code&gt;, each mark &lt;code&gt;step-2&lt;/code&gt; as a dependency. Once &lt;code&gt;step-2&lt;/code&gt; completes, &lt;code&gt;step-3a&lt;/code&gt; and &lt;code&gt;step-3b&lt;/code&gt; will run in parallel.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;step-3a &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; step-2 &lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; [cluster-path friend-path]]
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;cluster-users user-path cluster-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;count-friends user-path friend-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

step-3b &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; step-2 &lt;span style=&quot;color: #008b8b;&quot;&gt;:tmp-dirs&lt;/span&gt; age-path]
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;examine-ages user-path age-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The final step marks its dependencies as &lt;code&gt;:all&lt;/code&gt;. This signifies that the step must wait for every step defined above it to complete before running. Again, if &lt;code&gt;final-step&lt;/code&gt; fails and the workflow restarts, all previous successful steps will be skipped.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;final-step &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;[&lt;span style=&quot;color: #008b8b;&quot;&gt;:deps&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:all&lt;/span&gt;]
              &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;big-analysis cluster-path
                            friend-path
                            age-path
                            output-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;In Conclusion&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
I&#39;m quite excited about the Cascalog-contrib project and hope you all make heavy use of it as its functionality grows. In the short-term, I&#39;m planning on hooking Cascalog in to &lt;a href=&quot;http://incanter.org/&quot;&gt;Incanter&#39;s&lt;/a&gt; amazing visualization suite through the &lt;code&gt;cascalog.incanter&lt;/code&gt; module.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Testing Cascalog with Midje</title>
   <link href="http://sritchie.github.com/2011/09/30/testing-cascalog-with-midje/"/>
   <updated>2011-09-30T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2011/09/30/testing-cascalog-with-midje</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;30 Sept 2011 - San Francisco&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Testing Cascalog with Midje&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
I&#39;ve been working on a Cascalog testing suite these past few weeks, an extension to Brian Marick&#39;s &lt;a href=&quot;https://github.com/marick/Midje&quot;&gt;Midje&lt;/a&gt;, that eases much of the pain of testing MapReduce workflows. I think a lot of the dull work we see in the Hadoop community is a direct result of fear. Without proper tests, Hadoop developers can&#39;t help but be scared of making changes to production code. When creativity might bring down a workflow, it&#39;s easiest to get it working once and leave it alone.
&lt;/p&gt;

&lt;p&gt;
The antidote to all of this fear is a functional testing suite. As I discussed in &lt;a href=&quot;http://sritchie.github.com/2011/09/29/getting-creative-with-mapreduce.html&quot;&gt;Getting Creative with MapReduce&lt;/a&gt;, Hadoop workflows are difficult to test at all; testing application logic in isolation of data storage is impossible.
&lt;/p&gt;

&lt;p&gt;
Cascalog is free of this weakness. &lt;a href=&quot;https://github.com/sritchie/midje-cascalog&quot;&gt;midje-cascalog&lt;/a&gt; allows you to test Cascalog queries as pure functions, both in isolation and as components of more complicated workflows. the resulting tests are truly beautiful.
&lt;/p&gt;

&lt;p&gt;
I&#39;ll start by introducing midje-cascalog&#39;s testing operators, then move on to a Cascalog implementation of Word Count, tests included. You can find all source code from this post &lt;a href=&quot;https://github.com/sritchie/cascalog-testing-demo&quot;&gt;on github&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;Testing Operators&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
In this section, I&#39;ll discuss midje-cascalog&#39;s testing operators: &lt;code&gt;fact?-&lt;/code&gt; and &lt;code&gt;fact?&amp;lt;-&lt;/code&gt;. (The syntax mirrors &lt;code&gt;?-&lt;/code&gt; and &lt;code&gt;?&amp;lt;-&lt;/code&gt;, Cascalog&#39;s &lt;a href=&quot;http://www.assembla.com/spaces/cascalog/wiki/Defining_and_executing_queries&quot;&gt;query execution operators&lt;/a&gt;.) These operators provide the abstractions necessary for testing complex Cascalog workflows. Add them to your namespace by including &lt;code&gt;(:use midje.cascalog)&lt;/code&gt; in the namespace header.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-1-1&quot;&gt;fact?-&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-1-1&quot;&gt;
&lt;p&gt;
Let&#39;s begin by defining a function to test:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;mk-inc-query&lt;/span&gt; [src]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?a ?b]
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?a&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;inc&lt;/span&gt; ?a &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?b&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;mk-inc-query&lt;/code&gt; accepts a source of 1-tuples and returns a query that generates 2-tuples. To test that &lt;code&gt;mk-inc-query&lt;/code&gt; actually does this, you need to:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;supply &lt;code&gt;mk-inc-query&lt;/code&gt; with tuples and
&lt;/li&gt;
&lt;li&gt;check that it produces an expected set of result tuples.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Each of the following forms uses the &lt;code&gt;fact?-&lt;/code&gt; operator to state a distinct &quot;fact&quot; about our query. &lt;code&gt;fact?-&lt;/code&gt; expects a sequence of result tuples followed by the query tasked with producing them.
&lt;/p&gt;

&lt;p&gt;
These two facts about &lt;code&gt;mk-inc-query&lt;/code&gt; are true, and pass:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;The query returned by (mk-inc-query [[1]]),&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;when executed,&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;returns a single tuple: [1 2]&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- [[1 2]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;mk-inc-query [[1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;fact is true!&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;The query returned by (mk-inc-query [[1] [10]]),&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;When executed,&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;returns two tuples: [10 11] and [1 2]&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- [[10 11]
         [1 2]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;mk-inc-query [[1] [10]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;fact is true!&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This fact is false, and fails:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;The query returned by (mk-inc-query [[1]]),&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;when executed,&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;returns a single tuple: [&quot;fail!&quot; 10].&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;fail!&quot;&lt;/span&gt; 10]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;mk-inc-query [[1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;fact is FALSE!&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;fact?-&lt;/code&gt; can take multiples pairs of result-tuples and queries:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;Same as two true facts above.&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- [[1 2]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;mk-inc-query [[1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

        [[10 11] [1 2]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;mk-inc-query [[1] [10]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;both facts are true!&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Strings are ignored wherever they appear, so feel free to pepper your facts with comments.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;These results:&quot;&lt;/span&gt;
        [[1 2]]

        &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Are produced by this query:&quot;&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;mk-inc-query [[1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Note that facts don&#39;t have to be top level forms. It&#39;s perfectly acceptable to wrap facts in &lt;code&gt;let&lt;/code&gt;, if it makes the test clearer:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src     [[1]]
      results [[1 2]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- results &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;mk-inc-query src&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1-1-1&quot; class=&quot;outline-5&quot;&gt;
&lt;h5 id=&quot;sec-1-1-1-1&quot;&gt;Custom Log Levels&lt;/h5&gt;
&lt;div class=&quot;outline-text-5&quot; id=&quot;text-1-1-1-1&quot;&gt;
&lt;p&gt;
Cascalog pipes quite a bit of logging to &lt;code&gt;stdout&lt;/code&gt;. Facts suppress this logging by default, only showing entries with a FATAL log level.
&lt;/p&gt;

&lt;p&gt;
If you want to see more information on fact execution, you customize the log level by placing a keyword at the beginning of your fact:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #008b8b;&quot;&gt;:info&lt;/span&gt;
        [[1 2]] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;mk-inc-query [[1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
As of version 0.2.1, &lt;code&gt;midje-cascalog&lt;/code&gt; supports the following log-level keywords, and defaults to &lt;code&gt;:fatal&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:off&lt;/span&gt;
&lt;span style=&quot;color: #008b8b;&quot;&gt;:fatal&lt;/span&gt;
&lt;span style=&quot;color: #008b8b;&quot;&gt;:warn&lt;/span&gt;
&lt;span style=&quot;color: #008b8b;&quot;&gt;:info&lt;/span&gt;
&lt;span style=&quot;color: #008b8b;&quot;&gt;:debug&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-1-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-1-2&quot;&gt;fact?&amp;lt;-&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-1-2&quot;&gt;
&lt;p&gt;
The &lt;code&gt;fact?&amp;lt;-&lt;/code&gt; operator allows you to define a test a query within the same form. The following two facts are equivalent:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src [[1]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- [[1 2]]
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?a ?b]
              &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?a&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
              &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;inc&lt;/span&gt; ?a &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?b&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src [[1]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?&amp;lt;- [[1 2]]
           [?a ?b]
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?a&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;inc&lt;/span&gt; ?a &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?b&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Where &lt;code&gt;fact?-&lt;/code&gt; is useful for testing full queries and workflows, I find &lt;code&gt;fact?&amp;lt;-&lt;/code&gt; useful mostly for testing how &lt;code&gt;def*op&lt;/code&gt; functions behave inside of queries.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-1-3&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-1-3&quot;&gt;future-fact?- and future-fact?&amp;lt;-&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-1-3&quot;&gt;
&lt;p&gt;
If you want to stub out an unfinished test and prevent it from throwing errors, you can use &lt;code&gt;future-fact?-&lt;/code&gt;, like so:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;future-fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;unwritten-query will convert input integer tuples to&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;               strings.&quot;&lt;/span&gt;
               [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt;]]
               &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;unwritten-query [[1] [2]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src [[1] [2]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;future-fact?&amp;lt;- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;num-&amp;gt;string is unwritten.&quot;&lt;/span&gt;
                  [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt;]]
                  [?string]
                  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?num&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;num-&amp;gt;string ?string&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;future-fact?-&lt;/code&gt; and &lt;code&gt;future-fact?&amp;lt;-&lt;/code&gt; prevent their forms from being evaluated.
&lt;/p&gt;

&lt;p&gt;
If you include a string at the beginning of a stubbed fact, it shows up in Midje&#39;s test report looking like this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-text&quot;&gt;WORK TO DO: unwritten-query will convert input integer tuples to strings.
WORK TO DO: num-&amp;gt;string is unwritten.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The &lt;code&gt;fact?-&lt;/code&gt; and &lt;code&gt;fact?&amp;lt;-&lt;/code&gt; operators provide the tools necessary to test complex MapReduce workflows as pure functions. Let&#39;s expand on these concepts by creating a small project with Cascalog code we&#39;d like to test.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;Example Project&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;/div&gt;&lt;div id=&quot;outline-container-sec-1-2-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-2-1&quot;&gt;Dependencies&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-2-1&quot;&gt;
&lt;p&gt;
To add &lt;code&gt;midje-cascalog&lt;/code&gt; support to your own project, add these entries to to the &lt;code&gt;:dev-dependencies&lt;/code&gt; vector within &lt;code&gt;project.clj&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;[lein-midje &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;1.0.3&quot;&lt;/span&gt;]
[midje-cascalog &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;0.2.1&quot;&lt;/span&gt;]
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And add &lt;code&gt;(:use [midje sweet cascalog])&lt;/code&gt; to the namespace declaration of each of your testing namespaces.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-2-2&quot;&gt;Implementing Word Count&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-2-2&quot;&gt;
&lt;p&gt;
Let&#39;s begin with an implementation of word count, the typical &quot;Hello World!&quot; of MapReduce. A word counting application must be able to read in any number of textfiles and generate tuples of the form &lt;code&gt;[word, count]&lt;/code&gt; for each distinct word across all files.
&lt;/p&gt;

&lt;p&gt;
The following code accomplishes this nicely. (Bear with me! a detailed discussion follows the code block.)
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;ns&lt;/span&gt; cascalog.testing-demo.core
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:use&lt;/span&gt; cascalog.api&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:require&lt;/span&gt; [cascalog.ops &lt;span style=&quot;color: #008b8b;&quot;&gt;:as&lt;/span&gt; c]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:gen-class&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defmapcatop&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;split&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Accepts a sentence 1-tuple, splits that sentence on whitespace, and&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  emits a single 1-tuple for each word.&quot;&lt;/span&gt;
  [&lt;span style=&quot;color: #483d8b;&quot;&gt;^String&lt;/span&gt; sentence]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;seq&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;.split&lt;/span&gt; sentence &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;\\s+&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;wc-query&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Returns a subquery that generates counts for every word in&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;    the text-files located at `text-path`.&quot;&lt;/span&gt;
  [text-path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline text-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;]
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?word ?count]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?textline&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;split ?textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?word&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;c/count ?count&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;-main&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Accepts the following arguments:&lt;/span&gt;

&lt;span style=&quot;color: #8b2252;&quot;&gt;   - text-path (path to a textfile, or directory with textfiles)&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;   - results-path (location of textfile containing results)&lt;/span&gt;

&lt;span style=&quot;color: #8b2252;&quot;&gt;     And prints lines of the form \&quot;word count\&quot; to a textfile at&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;     results-path. Each distinct word in the textfiles at text-path&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;     gets a count.&quot;&lt;/span&gt;
  [text-path results-path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;?- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline results-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query text-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The &lt;code&gt;-main&lt;/code&gt; function is the entry point to the word counting program. &lt;code&gt;-main&lt;/code&gt; passes &lt;code&gt;text-path&lt;/code&gt; on to &lt;code&gt;wc-query&lt;/code&gt;, and writes all tuples generated by the returned query to a text file at &lt;code&gt;results-path&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
All of our program&#39;s application logic occurs in the query returned by &lt;code&gt;wc-query&lt;/code&gt;; this is the most important function to test. Let&#39;s discuss how &lt;code&gt;wc-query&lt;/code&gt; works:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;wc-query&lt;/code&gt; is a function that returns a subquery.
&lt;/li&gt;
&lt;li&gt;The function calls &lt;code&gt;hfs-textline&lt;/code&gt; internally to generate a source of &lt;code&gt;?sentence&lt;/code&gt; tuples.
&lt;/li&gt;
&lt;li&gt;These sentences are passed into &lt;code&gt;split&lt;/code&gt;, a Cascalog function that creates words from sentences, like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [sentence [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two words&quot;&lt;/span&gt;]]
      words    [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;words&quot;&lt;/span&gt;]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?&amp;lt;- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;split converts a sentence into words.&quot;&lt;/span&gt;
           words
           [?word]
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;sentence ?sentence&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;split ?sentence &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?word&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Each word gets a count via the &lt;code&gt;cascalog.ops/count&lt;/code&gt; function
&lt;/li&gt;
&lt;li&gt;The subquery returns each &lt;code&gt;[?word ?count]&lt;/code&gt; pair.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
This logic looks right, but the only way to tell is to write a series of facts and see if they&#39;re true.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2-3&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-2-3&quot;&gt;Testing Wordcount&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-2-3&quot;&gt;
&lt;p&gt;
Let&#39;s put our tests in &lt;code&gt;./test/cascalog/testing_demo/core_test.clj&lt;/code&gt; (mirroring the &lt;code&gt;core.clj&lt;/code&gt;, with &lt;code&gt;_test&lt;/code&gt; tacked on):
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;ns&lt;/span&gt; cascalog.testing-demo.core-test
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:use&lt;/span&gt; cascalog.testing-demo.core
        cascalog.api
        [midje sweet cascalog]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:require&lt;/span&gt; [cascalog.ops &lt;span style=&quot;color: #008b8b;&quot;&gt;:as&lt;/span&gt; c]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Here&#39;s an initial try at a test of &lt;code&gt;wc-query&lt;/code&gt; using &lt;code&gt;fact?-&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;/path/to/textfile points to a textfile with a single line:&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;&quot;another another word&quot;&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;wc-query should count words from all lines of text at&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;        /path/to/textfile.&quot;&lt;/span&gt;
        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;word&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;another&quot;&lt;/span&gt; 2]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;/path/to/textfile&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;FALSE!&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This fact fails. Here are a few of its problems:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;The fact depends on the way tuples are stored; it depends on an outside textfile located at a hard-coded path. If the textfile disappears, the fact will fail whether or not the logic of &lt;code&gt;wc-query&lt;/code&gt; is correct.
&lt;/li&gt;
&lt;li&gt;The fact depends on the correctness of &lt;code&gt;hfs-textline&lt;/code&gt;. if &lt;code&gt;hfs-textline&lt;/code&gt; fails, our fact fails.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;b&gt;Testing wc-query in isolation is difficult!&lt;/b&gt; How can one test the logic of &lt;code&gt;wc-query-&lt;/code&gt; without regard to how lines of text are stored?
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;Mocking with Midje&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
The solution lies in Midje&#39;s ability to mock out a function&#39;s return values. Midje can hijack &lt;code&gt;hfs-textline&lt;/code&gt; and force it to return anything you choose inside the body of a fact.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-3-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-3-1&quot;&gt;provided&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-3-1&quot;&gt;
&lt;p&gt;
Using Midje&#39;s &lt;code&gt;provided&lt;/code&gt; form, the above fact passes:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;wc-query should count words from all input sentences.&quot;&lt;/span&gt;
        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;word&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;another&quot;&lt;/span&gt; 2]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;another another word&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This fact states
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;when &lt;code&gt;wc-query&lt;/code&gt; is called with &lt;code&gt;:path&lt;/code&gt;,
&lt;/li&gt;
&lt;li&gt;it will produce two tuples: &lt;code&gt;[&quot;word&quot; 1]&lt;/code&gt; and &lt;code&gt;[&quot;another&quot; 2]&lt;/code&gt;,
&lt;/li&gt;
&lt;li&gt;provided &lt;code&gt;(hfs-textline :path)&lt;/code&gt; produces a single tuple: &lt;code&gt;[&quot;another another word&quot;]&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Here&#39;s another true fact about &lt;code&gt;wc-query&lt;/code&gt; that uses multiple input sentences:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;short-sentences&lt;/span&gt;
  [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;this is a sentence sentence&quot;&lt;/span&gt;]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;sentence with this is repeated&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;short-wordcounts&lt;/span&gt;
  [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;sentence&quot;&lt;/span&gt; 3]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;repeated&quot;&lt;/span&gt; 1]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;is&quot;&lt;/span&gt; 2]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;a&quot;&lt;/span&gt; 1]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;this&quot;&lt;/span&gt; 2]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;with&quot;&lt;/span&gt; 1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;when =wc-query= is called with =:text-path=&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;it will produce =short-sentences=,&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;provided =(hfs-textline :text-path)= produces =short-wordcounts=.&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- short-wordcounts &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:text-path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:text-path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; short-sentences&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
A &lt;code&gt;provided&lt;/code&gt; form only applies to the result-query pair directly above. The first fact is false, while the second fact is true:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [sentence [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two words&quot;&lt;/span&gt;]]
      results  [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;words&quot;&lt;/span&gt; 1]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;provided form won&#39;t apply here!&quot;&lt;/span&gt;
          results &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;false&lt;/span&gt;

          &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;provided applies here.&quot;&lt;/span&gt;
          results &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
            &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; sentence&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-3-2&quot;&gt;Mocking Arguments&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-3-2&quot;&gt;
&lt;p&gt;
In the above facts, I used keywords (&lt;code&gt;:path&lt;/code&gt;) as mocking arguments. Any form that evaluates to itself can be used as a mocking argument. In vanilla Clojure, this includes strings, numbers and keywords. Midje adds any symbol surrounded by dots (&lt;code&gt;..path..&lt;/code&gt;, &lt;code&gt;.path.&lt;/code&gt;, etc.) to this mix.
&lt;/p&gt;

&lt;p&gt;
These facts about &lt;code&gt;wc-query&lt;/code&gt; from above are all true, and identical:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Mocking with keywords,&quot;&lt;/span&gt;
        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt; 1]] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

        &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;strings,&quot;&lt;/span&gt;
        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt; 1]] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

        &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;numbers,&quot;&lt;/span&gt;
        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt; 1]] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query 100&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline 100&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

        &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;and Midje dotted symbols.&quot;&lt;/span&gt;
        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt; 1]] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query ..path..&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline ..path..&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;one&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3-3&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-3-3&quot;&gt;against-background&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-3-3&quot;&gt;
&lt;p&gt;
As discussed, the &lt;code&gt;provided&lt;/code&gt; form only applies to the result-query pair directly above. This limitation can make for repetitive facts, when each fact depends on a mocked result:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;text-&amp;gt;words&lt;/span&gt; [path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;]
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?word]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?sentence&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;split ?sentence &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?word&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008b8b;&quot;&gt;:distinct&lt;/span&gt; false&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [sentence [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two two&quot;&lt;/span&gt;]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text-&amp;gt;words cuts text into words.&quot;&lt;/span&gt;
          [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt;]] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;text-&amp;gt;words &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
            &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; sentence&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

          &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;wc-query converts a sentence into words.&quot;&lt;/span&gt;
          [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt; 2]] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
            &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; sentence&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Midje allows facts to share mocked functions with &lt;code&gt;against-background&lt;/code&gt;. An &lt;code&gt;against-background&lt;/code&gt; form placed anywhere inside the body of &lt;code&gt;fact?-&lt;/code&gt; will apply to all facts inside the form:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [sentence [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two two&quot;&lt;/span&gt;]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;text-&amp;gt;words cuts text into words.&quot;&lt;/span&gt;
          [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt;]] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;text-&amp;gt;words &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

          &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;wc-query converts a sentence into words.&quot;&lt;/span&gt;
          [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt; 2]]
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

          &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;wc-query fact with difference inputs.&quot;&lt;/span&gt;
          [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;what&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;a&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;world!&quot;&lt;/span&gt; 1]]
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
            &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;what a world!&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;against-background
            &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; sentence&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Note that the third of the three above facts used its own &lt;code&gt;provided&lt;/code&gt; form. When the two forms are mixed, &lt;code&gt;provided&lt;/code&gt; takes precedence, shadowing &lt;code&gt;against-background&lt;/code&gt; if need be (as above).
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;Collection Checkers&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
For the next set of facts, let&#39;s introduce a larger set of input sentences:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;longer-sentences&lt;/span&gt;
  [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Call me Ishmael. Some years ago -- never mind how long&quot;&lt;/span&gt;]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;precisely -- having little or no money in my purse, and&quot;&lt;/span&gt;]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;nothing particular to interest me on shore, I thought I&quot;&lt;/span&gt;]
   [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;would sail about a little and see the watery part of the world.&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
One issue with the above facts is that they use very small input sentences. &lt;code&gt;wc-query&lt;/code&gt; will produce a rather large sequence of &lt;code&gt;&amp;lt;word, count&amp;gt;&lt;/code&gt; pairs for a moderate number of input sentences. Facts like this are overwhelming:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Ishmael.&quot;&lt;/span&gt; 1]
         [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Some&quot;&lt;/span&gt; 1]
         [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;a&quot;&lt;/span&gt; 1]
         [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;about&quot;&lt;/span&gt; 1]
         [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;ago&quot;&lt;/span&gt; 1]
         &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;and on and on...&lt;/span&gt;
         ]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; longer-sentences&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
To solve this, Midje provides a number of collection checkers that provide you with finer control over how queries are compared with result sequences.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-4-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-4-1&quot;&gt;just&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-4-1&quot;&gt;
&lt;p&gt;
&lt;code&gt;just&lt;/code&gt; is the default checker for &lt;code&gt;fact?-&lt;/code&gt; and &lt;code&gt;fact?&amp;lt;-&lt;/code&gt;; bare vectors of tuples resolve to &lt;code&gt;(just result-vec :in-any-order)&lt;/code&gt;. The following three facts are equivalent:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src   [[1] [2]]
      query &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?a ?b]
                &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;src ?a&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;inc&lt;/span&gt; ?a &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; ?b&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Just form, fully qualified.&quot;&lt;/span&gt;
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;just [[2 3] [1 2]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-any-order&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; query &lt;span style=&quot;color: #b22222;&quot;&gt;;;&lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;

          &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Wrapping tuples in a set is identical to including&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;           the :in-any-order modifier.&quot;&lt;/span&gt;
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;just #{[2 3] [1 2]}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; query &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;

          &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;midje-cascalog lets us drop these wrappers.&quot;&lt;/span&gt;
          [[2 3] [1 2]] query&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Each of these facts checks that its subquery returns &lt;code&gt;[2 3]&lt;/code&gt; &lt;code&gt;[1 2]&lt;/code&gt; exclusively, in any order. Any missing or extra tuples in the result vector will cause a failure.
&lt;/p&gt;

&lt;p&gt;
Note that dropping the &lt;code&gt;:in-any-order&lt;/code&gt; modifier (or the set wrapper) will cause facts to fail if ordering doesn&#39;t match. This makes sense sometimes when checking against top-n queries, as noted in the discussion below on &lt;a href=&quot;#sec-1-4-3&quot;&gt;has-prefix&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-4-2&quot;&gt;contains&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-4-2&quot;&gt;
&lt;p&gt;
The &lt;code&gt;contains&lt;/code&gt; form allows facts to check against a subset of query tuples. By default, &lt;code&gt;contains&lt;/code&gt; requires result tuples to be contiguous and ordered: &lt;code&gt;[1 2]&lt;/code&gt; within &lt;code&gt;[3 4 1 2 1]&lt;/code&gt;, for example.
&lt;/p&gt;

&lt;p&gt;
These restrictions are quite limiting for most Cascalog queries. The following two facts avoid both restrictions:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;contains #{[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;sail&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Ishmael.&quot;&lt;/span&gt; 1]} &lt;span style=&quot;color: #008b8b;&quot;&gt;:gaps-ok&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;

        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;contains [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;sail&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Ishmael.&quot;&lt;/span&gt; 1]] &lt;span style=&quot;color: #008b8b;&quot;&gt;:gaps-ok&lt;/span&gt; &lt;span style=&quot;color: #008b8b;&quot;&gt;:in-any-order&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;

        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;against-background
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; longer-sentences&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The above facts test that both &lt;code&gt;[&quot;sail&quot; 1]&lt;/code&gt; and &lt;code&gt;[&quot;Ishmael.&quot; 1]&lt;/code&gt; appear somewhere in the results, in any order.
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Wrapping the result tuples in a set (vs. a vector), or adding the &lt;code&gt;:in-any-order&lt;/code&gt; keyword, relaxes the ordering restriction.
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;:gaps-ok&lt;/code&gt; keyword relaxes the restriction that tuples must contiguous.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4-3&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-4-3&quot;&gt;has-prefix&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-4-3&quot;&gt;
&lt;p&gt;
&lt;code&gt;has-prefix&lt;/code&gt; checks that the supplied tuple sequence appears at the beginning of the query&#39;s results. &lt;code&gt;has-prefix&lt;/code&gt; only makes sense with queries that return sorted tuples.
&lt;/p&gt;

&lt;p&gt;
The following fact states that &lt;code&gt;[&quot;--&quot; 2]&lt;/code&gt;, &lt;code&gt;[&quot;I&quot; 2]&lt;/code&gt; and &lt;code&gt;[&quot;and&quot; 2]&lt;/code&gt;, in order, are the three most common words across all words in &lt;code&gt;longer-sentences&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;has-prefix [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;--&quot;&lt;/span&gt; 2] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;I&quot;&lt;/span&gt; 2] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;and&quot;&lt;/span&gt; 2]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
            &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;c/first-n 10 &lt;span style=&quot;color: #008b8b;&quot;&gt;:sort&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;?count&quot;&lt;/span&gt;] &lt;span style=&quot;color: #008b8b;&quot;&gt;:reverse&lt;/span&gt; true&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; longer-sentences&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4-4&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-4-4&quot;&gt;has-suffix&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-4-4&quot;&gt;
&lt;p&gt;
&lt;code&gt;has-suffix&lt;/code&gt; checks that the supplied tuple sequence appears at the end of the query&#39;s results.
&lt;/p&gt;

&lt;p&gt;
The following fact states that &lt;code&gt;[&quot;world.&quot; 1]&lt;/code&gt;, &lt;code&gt;[&quot;would&quot; 1]&lt;/code&gt; and &lt;code&gt;[&quot;years&quot; 2]&lt;/code&gt;, in order, are the last three words (by alphabetical order) across all words in &lt;code&gt;longer-sentences&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;has-suffix [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;world.&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;would&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;years&quot;&lt;/span&gt; 1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:text-path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
            &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;c/first-n 100 &lt;span style=&quot;color: #008b8b;&quot;&gt;:sort&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;?word&quot;&lt;/span&gt;]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:text-path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; longer-sentences&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
As with &lt;code&gt;has-prefix&lt;/code&gt;, facts making use of &lt;code&gt;has-suffix&lt;/code&gt; only make sense when specifically testing tuple ordering.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-5&quot;&gt;Tabular&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-5&quot;&gt;
&lt;p&gt;
In certain cases, you might like to test a single query against a wide range of inputs and outputs. This quickly grows repetitive:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;mock&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;it&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;out!&quot;&lt;/span&gt; 1]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;mock it out!&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;;&lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;

        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt; 3]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two two two&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;;&lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;

        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;M.M&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;nathan&quot;&lt;/span&gt; 1]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;nathan M.M&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Gah! &lt;code&gt;against-background&lt;/code&gt; doesn&#39;t work here, since these facts mock against different sentences each time.
&lt;/p&gt;

&lt;p&gt;
Midje&#39;s &lt;code&gt;tabular&lt;/code&gt; form provides an elegant way to collapse this repetition:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;tabular
 &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Tabular generates lots of facts, one for each set of&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;         substitutions in the table below.&quot;&lt;/span&gt;
         ?results
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[?sentence]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
 ?sentence       ?results
 &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;mock it out!&quot;&lt;/span&gt;  [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;mock&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;it&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;out!&quot;&lt;/span&gt; 1]]
 &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two two two&quot;&lt;/span&gt;   [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;two&quot;&lt;/span&gt; 3]]
 &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;nathan M.M&quot;&lt;/span&gt;    [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;M.M&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;nathan&quot;&lt;/span&gt; 1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;3 true facts&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
(This one&#39;s a little involved, but the results are really beautiful.)
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;tabular&lt;/code&gt; accepts three types of arguments:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;a single &lt;code&gt;fact?-&lt;/code&gt; or &lt;code&gt;fact?&amp;lt;-&lt;/code&gt; templating form
&lt;/li&gt;
&lt;li&gt;a number of &quot;templating variables&quot; that start with &lt;code&gt;?&lt;/code&gt; (&lt;code&gt;?sentence&lt;/code&gt; and &lt;code&gt;?results&lt;/code&gt;, in the above fact)
&lt;/li&gt;
&lt;li&gt;any number of rows of substitutions (the above fact has three)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
and generates a separate fact for every substitution row. It does this by substituting each value into the templating form in place of the header variable at the top of column.
&lt;/p&gt;

&lt;p&gt;
The first fact generated by the above tabular fact looks like this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;tabular
 &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;Tabular takes this templating form:&lt;/span&gt;
 &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Tabular generates lots of facts, one for each set of&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;           substitutions in the table below.&quot;&lt;/span&gt;
         ?results
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
           &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[?sentence]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

 &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;and substitutes these variables:&lt;/span&gt;
 ?sentence       ?results
 &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;mock it out!&quot;&lt;/span&gt;  [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;mock&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;it&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;out!&quot;&lt;/span&gt; 1]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;to produce this fact:&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;mock&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;it&quot;&lt;/span&gt; 1] [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;out!&quot;&lt;/span&gt; 1]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wc-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;mock it out!&quot;&lt;/span&gt;]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Any variable prefixed by &lt;code&gt;?&lt;/code&gt; that appears inside both the fact template AND the header variables row is earmarked for substitution. This means that cascalog dynamic variables are totally safe, and play well with tabular.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-6&quot;&gt;Running Tests&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-6&quot;&gt;
&lt;/div&gt;&lt;div id=&quot;outline-container-sec-1-6-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-6-1&quot;&gt;lein-midje&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-6-1&quot;&gt;
&lt;p&gt;
Once you write facts within a project, you can use &lt;a href=&quot;https://github.com/marick/Midje/wiki/Lein-midje&quot;&gt;lein-midje&lt;/a&gt; to run them all and generate a summary like this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-text&quot;&gt;Checking function: (midje.sweet/just [[&quot;Ishmael.&quot; 1] [&quot;Some&quot; 1] [&quot;a&quot; 1] [&quot;about&quot; 1] [&quot;ago&quot; 1]] :in-any-order)
The checker said this about the reason:
    Expected five elements. There were thirty-nine.
FAILURE: 6 facts were not confirmed. (But 37 were.)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
If you&#39;re using the leiningen build manager, follow these steps:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Add  &lt;code&gt;[lein-midje &quot;1.0.7&quot;]&lt;/code&gt; to the &lt;code&gt;:dev-dependencies&lt;/code&gt; entry in your &lt;code&gt;project.clj&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;lein midje&lt;/code&gt; at the command line in your project&#39;s root directory.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
This command runs all facts and tests in the project and prints a summary of all results to stdout.
&lt;/p&gt;

&lt;p&gt;
If you&#39;re using Cake, follow the steps on the &lt;a href=&quot;https://github.com/marick/Midje/wiki/Cake-midje&quot;&gt;Midje wiki&lt;/a&gt; for installing and running &lt;code&gt;cake midje&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-6-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-6-2&quot;&gt;Interaction with clojure.test&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-6-2&quot;&gt;
&lt;p&gt;
If you currently write &lt;code&gt;deftest&lt;/code&gt; style tests using clojure.test, check out &lt;a href=&quot;https://github.com/marick/Midje/wiki/Migrating-from-clojure.test&quot;&gt;Midje&#39;s tips&lt;/a&gt; on integration. The two modes work very well together. &lt;code&gt;lein midje&lt;/code&gt; and &lt;code&gt;cake midje&lt;/code&gt; will evaluate all &lt;code&gt;deftest&lt;/code&gt; forms inside of a project and include the results in its report.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-7&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-7&quot;&gt;In Conclusion&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-7&quot;&gt;
&lt;p&gt;
I believe that midje-cascalog is the most advanced MapReduce testing suite available today. The primitives discussed here make testing Cascalog queries a joy; the confidence that comes from fully tested components is a prerequisitive for creative work at large scale.
&lt;/p&gt;

&lt;p&gt;
Please let me know what you think of the project! I&#39;m happy to extend midje-cascalog in any way that helps the cause. Have fun testing!
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Getting Creative with MapReduce</title>
   <link href="http://sritchie.github.com/2011/09/29/getting-creative-with-mapreduce/"/>
   <updated>2011-09-29T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2011/09/29/getting-creative-with-mapreduce</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;29 Sept 2011 - San Francisco&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Getting Creative with MapReduce&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
One problem with many existing MapReduce abstraction layers is the utter difficulty of testing queries and workflows. End-to-end tests are maddening to craft in vanilla Hadoop and frustrating at best in Pig and Hive. The difficulty of testing MapReduce workflows makes it scary to change code, and &lt;b&gt;destroys your desire to be creative&lt;/b&gt;. A proper testing suite is an absolute prerequisite to doing creative work in big data.
&lt;/p&gt;

&lt;p&gt;
In this blog post, I aim to show how most of the difficulty of writing and testing MapReduce queries stems from the fact that Hadoop confounds application logic with decisions about data storage. These problems are the result of poorly implemented abstractions over the primitives of MapReduce, not problems with the core MapReduce algorithms.
&lt;/p&gt;

&lt;p&gt;
The &lt;a href=&quot;https://github.com/nathanmarz/cascalog&quot;&gt;Cascalog&lt;/a&gt; abstraction layer fixes this issue by separating logic from data, allowing you to play creatively at massive scale. (If you&#39;re not familiar with Cascalog, know that it&#39;s by far the most expressive MapReduce DSL in existence. We make heavy use of Cascalog here at Twitter. I recommend getting started with &lt;a href=&quot;http://nathanmarz.com/blog/introducing-cascalog-a-clojure-based-query-language-for-hado.html&quot;&gt;this introduction&lt;/a&gt;, then moving on to &lt;a href=&quot;http://www.assembla.com/wiki/show/d9Z8_q-Omr35zteJe5cbLr&quot;&gt;the wiki&lt;/a&gt;.)
&lt;/p&gt;

&lt;p&gt;
Cascalog and its testing suite, &lt;a href=&quot;https://github.com/sritchie/midje-cascalog&quot;&gt;midje-cascalog&lt;/a&gt;, allow us to test application logic in isolation; the resulting tests are truly beautiful.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;Current Approaches to MR Testing&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
Say you&#39;ve implemented &lt;a href=&quot;http://hadoop.apache.org/common/docs/current/mapred_tutorial.html#Example%3A+WordCount+v1.0&quot;&gt;wordcount in MapReduce&lt;/a&gt;, and are looking to write unit tests against your workflow. The Hadoop consulting giant &lt;a href=&quot;http://www.cloudera.com/&quot;&gt;Cloudera&lt;/a&gt; defines current best practices &lt;a href=&quot;http://www.cloudera.com/blog/2009/07/debugging-mapreduce-programs-with-mrunit/&quot;&gt;in this article&lt;/a&gt;:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
The current state of the art often involves writing a set of tests that each create a JobConf object, which is configured to use a mapper and reducer, and then set to use the LocalJobRunner (via &lt;code&gt;JobConf.set(”mapred.job.tracker”, “local”)&lt;/code&gt;). A MapReduce job will then run in a single thread, reading its input from test files stored on the local filesystem and writing its output to another local directory.
&lt;/p&gt;

&lt;p&gt;
This process provides a solid mechanism for end-to-end testing, but has several drawbacks. Developing new tests requires adding test inputs to files that are stored alongside one’s program. Validating correct output also requires filesystem access and parsing of the emitted data files. This involves writing a great deal of test harness code, which itself may contain subtle bugs. Finally, this process is slow. Each test requires several seconds to run. Users often find themselves aggregating several unrelated inputs into a single test (violating a unit testing principle of isolating unrelated tests) or performing less exhaustive testing due to the high barriers to test authorship.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
This is insane. Written this way, unit tests meant to target application logic are dwarfed by the maintenance they impose.
&lt;/p&gt;

&lt;p&gt;
Most real world MapReduce applications involve chained sequences of many queries; these flows are a nightmare to coordinate and test using the above methods. Perhaps for this reason, &lt;a href=&quot;http://www.cloudera.com/blog/2009/07/debugging-mapreduce-programs-with-mrunit/&quot;&gt;Cloudera&lt;/a&gt; recommends testing mappers and reducers separately:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
your &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;reduce()&lt;/code&gt; calls should still be tested individually, as the composition of separate classes may cause unintended bugs to surface.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Again, this is totally batshit crazy. MapReduce queries are functions that operate on datasets. Imagine writing a Clojure function and testing its lines one at a time, in isolation; the approach makes no sense.
&lt;/p&gt;

&lt;p&gt;
A well designed testing suite will examine each MapReduce query as the function that it is. Queries should be tested against a range of inputs, both in isolation and as components of more complicated workflows. It shouldn&#39;t matter how one&#39;s data is stored; query logic is independent of data storage, and should be tested as such.
&lt;/p&gt;

&lt;p&gt;
I&#39;d like to offer a more sane approach to MapReduce testing. In the following example, I&#39;ll write MapReduce query with Cascalog and test it using &lt;a href=&quot;https://github.com/sritchie/midje-cascalog&quot;&gt;midje-cascalog&lt;/a&gt;, a thin wrapper I wrote over the Midje testing DSL.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;Functional MapReduce Testing&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
Let&#39;s say you want to test a Cascalog workflow that examines your user datastore and returns the user with the greatest number of followers. Your workflow&#39;s top level query will generate a single tuple containing that user&#39;s name and follower-count. Here&#39;s the code:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;max-followers-query&lt;/span&gt; [datastore-path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [src &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;name-vars &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;complex-subquery datastore-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                       [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;?user&quot;&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;?follower-count&quot;&lt;/span&gt;]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;]
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;cascalog.ops/first-n src 1 &lt;span style=&quot;color: #008b8b;&quot;&gt;:sort&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;?follower-count&quot;&lt;/span&gt;] &lt;span style=&quot;color: #008b8b;&quot;&gt;:reverse&lt;/span&gt; true&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;max-followers-query&lt;/code&gt; is a function that returns a Cascalog subquery. It works like this:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;The function accepts a path, (&lt;code&gt;datastore-path&lt;/code&gt;) and passes it into a function called &lt;code&gt;complex-subquery&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;complex-subquery&lt;/code&gt; returns a subquery that generates 2-tuples; this subquery is passed into &lt;code&gt;name-vars&lt;/code&gt;.
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;name-vars&lt;/code&gt; binds this subquery to &lt;code&gt;src&lt;/code&gt; after naming its output variables &lt;code&gt;?user&lt;/code&gt; and &lt;code&gt;?follower-count&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;first-n&lt;/code&gt; returns a subquery that
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;sorts tuples from &lt;code&gt;src&lt;/code&gt; in reverse order by follower count, and
&lt;/li&gt;
&lt;li&gt;returns a single 2-tuple with the name and follower-count of our most popular user.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
At a high level, the subquery returned by &lt;code&gt;max-followers-query&lt;/code&gt; is responsible for a single piece of application logic:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;extracting the tuple with max &lt;code&gt;?follower-count&lt;/code&gt; from the tuples returned by &lt;code&gt;(complex-subquery datastore-path)&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
A correct test of &lt;code&gt;max-followers-query&lt;/code&gt; will test this piece of logic in isolation.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-2-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-2-1&quot;&gt;Cloudera&#39;s &quot;State of the Art&quot;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-2-1&quot;&gt;
&lt;p&gt;
If you were to follow Cloudera&#39;s advice on how to test this query, you would have to:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Dig through the code of &lt;code&gt;complex-subquery&lt;/code&gt;, and figure out what data it depends on to produce its tuples;
&lt;/li&gt;
&lt;li&gt;Set up temporary directories and SequenceFiles with appropriate starting tuples;
&lt;/li&gt;
&lt;li&gt;Execute the subquery returned by &lt;code&gt;max-followers-query&lt;/code&gt; into a temporary SequenceFile;
&lt;/li&gt;
&lt;li&gt;Read tuples out of this SequenceFile and test them against expected values;
&lt;/li&gt;
&lt;li&gt;Clean up all temporary paths and directories.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
This series of steps adds tremendous friction to the testing process, and obscures the purpose of the test. Moreover, the results of any test of &lt;code&gt;max-followers-query&lt;/code&gt; depend on any number of subqueries invoked the call to &lt;code&gt;complex-subquery&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
If your tests are this difficult to write, you&#39;re not going to write very many tests. You need tests to be creative; without tests, you won&#39;t change production queries in fear of introducing some bug you don&#39;t understand.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-2-2&quot;&gt;Fact-Based Testing&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-2-2&quot;&gt;
&lt;p&gt;
Midje circumvents all of this complexity by mocking out the result of &lt;code&gt;(complex-subquery datastore-path)&lt;/code&gt; and forcing it to return a specific Clojure sequence of &lt;code&gt;[?user ?follower-count]&lt;/code&gt; tuples.
&lt;/p&gt;

&lt;p&gt;
The following form is a Midje test, or &quot;fact&quot;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;fact?- &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Query should return a single tuple containing&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;        [most-popular-user, follower-count].&quot;&lt;/span&gt;
        [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;richhickey&quot;&lt;/span&gt; 2961]]
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;max-followers-query &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;provided
          &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;complex-subquery &lt;span style=&quot;color: #008b8b;&quot;&gt;:path&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; =&amp;gt; [[&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;sritchie09&quot;&lt;/span&gt; 180]
                                       [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;richhickey&quot;&lt;/span&gt; 2961]]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Facts make statements about queries. The fact passes if these statements are true and fails otherwise. The above fact states that
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;when &lt;code&gt;max-followers-query&lt;/code&gt; is called with the argument &lt;code&gt;:path&lt;/code&gt;,
&lt;/li&gt;
&lt;li&gt;it will produce &lt;code&gt;[ [ richhickey&quot; 2961] ]&lt;/code&gt;,
&lt;/li&gt;
&lt;li&gt;provided &lt;code&gt;(complex-subquery :path)&lt;/code&gt; produces &lt;code&gt;[[&quot;sritchie09&quot; 180] [&quot;richhickey&quot; 2961]]&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Fact-based testing separates application logic from the way data is stored. By mocking out &lt;code&gt;complex-subquery&lt;/code&gt;, our fact tests &lt;code&gt;max-followers-query&lt;/code&gt; in isolation and proves it correct for all expected inputs.
&lt;/p&gt;

&lt;p&gt;
This approach is not just better than the &quot;state of the art&quot; of MapReduce testing as defined by Cloudera; it completely obliterates the old way of thinking, and makes it possible to build very complex workflows with a minimum of uncertainty.
&lt;/p&gt;

&lt;p&gt;
Fact-based tests are the building blocks of rock-solid production workflows.
&lt;/p&gt;

&lt;p&gt;
&lt;i&gt;If you&#39;re interested in how to construct fact-based tests with midje-cascalog, I go into great detail on functional MapReduce testing idioms and methods in&lt;/i&gt; &lt;a href=&quot;http://sritchie.github.com/2011/09/30/testing-cascalog-with-midje.html&quot;&gt;Testing Cascalog with Midje&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Cascalog 1.8.1 Released</title>
   <link href="http://sritchie.github.com/2011/09/26/cascalog-181-released/"/>
   <updated>2011-09-26T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2011/09/26/cascalog-181-released</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;26 Sept 2011 - San Francisco&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Cascalog 1.8.1 Released&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
&lt;a href=&quot;http://nathanmarz.com/&quot;&gt;Nathan Marz&lt;/a&gt; and I are releasing Cascalog 1.8.1 today! We&#39;ve added a few interesting features, and I thought I&#39;d provide a bit more detail here for anyone interested.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;Cross Join&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
&lt;code&gt;cascalog.api&lt;/code&gt; now includes support for &lt;a href=&quot;http://en.wikipedia.org/wiki/Join_(SQL)#Cross_join&quot;&gt;cross-joins&lt;/a&gt;; just add &lt;code&gt;(cross-join)&lt;/code&gt; to your query as its own predicate.
&lt;/p&gt;

&lt;p&gt;
Think of a cross-join as a &quot;tuple comprehension&quot;, or cartesian product, with similar results to &lt;code&gt;clojure.core/for&lt;/code&gt;; it&#39;s not very efficient, as it forces all tuples through a single reducer (and causes a massive blowup in the number of tuples!). Here&#39;s an example:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let&lt;/span&gt; [a-src [[1] [2]]
      b-src [[3] [4]]]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;?&amp;lt;- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;stdout&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
       [?a ?b]
       &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;a-src ?a&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
       &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;b-src ?b&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
       &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;cross-join&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This results in the following on &lt;code&gt;stdout&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-text&quot;&gt;RESULTS
-----------------------
1       3
1       4
2       3
2       4
-----------------------
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
If you&#39;re interested, here&#39;s the implementation:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;cross-join&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [&lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt;] &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;identity&lt;/span&gt; 1 &lt;span style=&quot;color: #008b8b;&quot;&gt;:&amp;gt;&lt;/span&gt; _&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This is the only predicate macro I know of that can get away with no input OR output vars.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;defmain&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
When running a cascalog query on a cluster, usual practice is to include &lt;code&gt;(:gen-class)&lt;/code&gt; in the namespace form, and write a &lt;code&gt;-main&lt;/code&gt; method that gets AOT-compiled and called by Hadoop. This can be a little annoying, if you have a bunch of small queries you want to run.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;defmain&lt;/code&gt; lets you skip the &lt;code&gt;:gen-class&lt;/code&gt; form and write something like:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;inside myproject.jobs...  &lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defmain&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;FirstQuery&lt;/span&gt; [in-path out-path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;?- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline out-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;some-query in-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defmain&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;SecondQuery&lt;/span&gt; [out-path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;?&amp;lt;- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline out-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
       [?a ?b] ...&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Each &lt;code&gt;defmain&lt;/code&gt; will compile to a class with the supplied name, prefixed by the namespace. (&lt;code&gt;myproject.jobs.FirstQuery&lt;/code&gt; and &lt;code&gt;myproject.jobs.SecondQuery&lt;/code&gt;, in this example.)
&lt;/p&gt;

&lt;p&gt;
(As always, make sure to add the &lt;code&gt;:aot [myproject.jobs]&lt;/code&gt;, kv-pair to &lt;code&gt;project.clj&lt;/code&gt;, including each namespace containing a &lt;code&gt;defmain&lt;/code&gt; or &lt;code&gt;(:gen-class)&lt;/code&gt; in the vector. If you want to call some &lt;code&gt;defmain&lt;/code&gt; function directly, &lt;code&gt;(defmain Query ...)&lt;/code&gt; can be called from the REPL with &lt;code&gt;(Query-main ...)&lt;/code&gt;.) I recommend keeping your &lt;code&gt;defmain&lt;/code&gt; functions skinny, and testing the components it calls.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;with-serializations&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
Damn you, serializations. This one JobConf entry, &quot;io.serializations&quot;, has caused me much pain. We&#39;ve added &lt;code&gt;with-serializations&lt;/code&gt;, which makes the supplied Hadoop serializations available to all queries enclosed within the form. These forms nest, and play well with the existing &lt;code&gt;with-job-conf&lt;/code&gt;. For example:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;You can specify serializations in string form...&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;with-serializations [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;org.apache.hadoop.io.serializer.JavaSerialization&quot;&lt;/span&gt;]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?a] ...&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;... or directly, with the class.&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;import&lt;/span&gt; &#39;&lt;span style=&quot;color: #483d8b;&quot;&gt;org.apache.hadoop.io.serializer.JavaSerialization&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;with-serializations [&lt;span style=&quot;color: #483d8b;&quot;&gt;JavaSerialization&lt;/span&gt;]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?a] ...&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;Serializations nest and unique against each other!&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;with-job-conf {&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;io.serializations&quot;&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;my.ns.SomeSerialization&quot;&lt;/span&gt;}
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;with-serializations [&lt;span style=&quot;color: #483d8b;&quot;&gt;JavaSerialization&lt;/span&gt; &lt;span style=&quot;color: #483d8b;&quot;&gt;OtherSerialization&lt;/span&gt;]
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;with-serializations [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;my.ns.SomeSerialization&quot;&lt;/span&gt; &lt;span style=&quot;color: #483d8b;&quot;&gt;ThirdSerialization&lt;/span&gt;]
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&amp;lt;- [?a] ...&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;cascalog.ops/first-n&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
&lt;code&gt;first-n&lt;/code&gt; can now handle straight-up vectors, lists, and cascading taps, in addition to queries.
&lt;/p&gt;

&lt;p&gt;
Say we&#39;ve previously run a wordcount job that output &lt;code&gt;[?word ?count]&lt;/code&gt; 2-tuples to a sequencefile, and we want to pull the top 100 words by count. Here&#39;s how we do that with first-n:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;use&lt;/span&gt; &#39;cascalog.api&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;require&lt;/span&gt; &#39;[cascalog.ops &lt;span style=&quot;color: #008b8b;&quot;&gt;:as&lt;/span&gt; c]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;wordcount-tap&lt;/span&gt; [path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-seqfile path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;name-vars [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;?word&quot;&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;?count&quot;&lt;/span&gt;]&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defn&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;top-100&lt;/span&gt; [file-path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;c/first-n &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;wordcount-tap path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
             100
             &lt;span style=&quot;color: #008b8b;&quot;&gt;:sort&lt;/span&gt; [&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;?count&quot;&lt;/span&gt;]
             &lt;span style=&quot;color: #008b8b;&quot;&gt;:reverse&lt;/span&gt; true&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defmain&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;Top100&lt;/span&gt; [tuple-path results-path]
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;?- &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;hfs-textline results-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
      &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;top-100 tuple-path&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;first-n&lt;/code&gt; with vectors and lists is mostly interesting for testing purposes.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-5&quot;&gt;Other Bugfixes&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-5&quot;&gt;
&lt;p&gt;
Just a few bugfixes to note:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Fixed a bug preventing cascalog-taps in &lt;code&gt;(:trap (some-tap ...))&lt;/code&gt; option predicates.
&lt;/li&gt;
&lt;li&gt;Fixed bug preventing keywords as static arguments (as &lt;a href=&quot;https://github.com/nathanmarz/cascalog/blob/master/test/cascalog/api_test.clj#L439&quot;&gt;demonstrated here&lt;/a&gt;).
&lt;/li&gt;
&lt;li&gt;Failed cascading flows now always throw errors. (This is a workaround to a Cascading bug that allows some flows to fail silently.)
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Haskell in Emacs</title>
   <link href="http://sritchie.github.com/2011/09/25/haskell-in-emacs/"/>
   <updated>2011-09-25T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2011/09/25/haskell-in-emacs</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;Sept 25 2011 - San Francisco&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Haskell in Emacs&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
I spent some time today getting my emacs config set up to learn Haskell, and ran into a few issues; I figured I&#39;d go ahead and document the process here for everyone&#39;s enjoyment. We&#39;re going to install and configure Haskell mode, then add a few extensions that&#39;ll make learning Haskell fun and easy!
&lt;/p&gt;

&lt;p&gt;
I&#39;m currently running haskell-mode for emacs, with the &lt;code&gt;hs-lint&lt;/code&gt; plugin, Haskell support for FlyMake (which provides on-the-fly syntax checking from the Haskell compiler), and code autocompletion. The steps covered by this tutorial are:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Installing Haskell
&lt;/li&gt;
&lt;li&gt;Configuring Haskell-Mode for Emacs
&lt;/li&gt;
&lt;li&gt;Installing Haskell-Mode Extensions (Flymake support, &lt;code&gt;hs-lint&lt;/code&gt; and autocompletion)
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;Installing Haskell&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
Before any of this Emacs jazz, we have to get Haskell, of course. The easiest way to do is to download the &lt;a href=&quot;http://hackage.haskell.org/platform/&quot;&gt;Haskell Platform&lt;/a&gt;, a &quot;Batteries Included&quot; package of the Glasgow Haskell Compiler.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;Emacs Haskell Mode&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
The &lt;a href=&quot;http://www.haskell.org/haskellwiki/Haskell_mode_for_Emacs&quot;&gt;Haskell Mode for Emacs&lt;/a&gt; page at the Haskell wiki is a nice place to start your explorations, and describes how to install &lt;a href=&quot;http://projects.haskell.org/haskellmode-emacs/&quot;&gt;haskell-mode&lt;/a&gt;. I found it easier to use ELPA, the Emacs Lisp Package Archive (install instructions &lt;a href=&quot;http://tromey.com/elpa/install.html&quot;&gt;here&lt;/a&gt;). If you&#39;re using the &lt;a href=&quot;https://github.com/technomancy/emacs-starter-kit&quot;&gt;Emacs starter kit&lt;/a&gt;, you&#39;ve already got ELPA.
&lt;/p&gt;

&lt;p&gt;
Once elpa&#39;s all set, install &lt;code&gt;haskell-mode&lt;/code&gt; with the following:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;run &lt;code&gt;M-x package-list-packages&lt;/code&gt; in Emacs
&lt;/li&gt;
&lt;li&gt;tap &lt;code&gt;i&lt;/code&gt; by &lt;code&gt;haskell-mode&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;hit &lt;code&gt;x&lt;/code&gt; to the start the install.
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;Configuring emacs.el&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
Before we get into linting or any other customizations, Add the following to your emacs config (&lt;code&gt;~/.emacs&lt;/code&gt;, or &lt;code&gt;~/.emacs.d/init.el&lt;/code&gt;, if you&#39;re using the Starter kit):
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;add-hook &#39;haskell-mode-hook &#39;turn-on-haskell-doc-mode&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;hslint on the command line only likes this indentation mode;&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;alternatives commented out below.&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;add-hook &#39;haskell-mode-hook &#39;turn-on-haskell-indentation&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;;&lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;(add-hook &#39;haskell-mode-hook &#39;turn-on-haskell-indent)&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;;&lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;(add-hook &#39;haskell-mode-hook &#39;turn-on-haskell-simple-indent)&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;Ignore compiled Haskell files in filename completions&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;add-to-list &#39;completion-ignored-extensions &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;.hi&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
At this point, you should be able to start using Haskell in emacs. Let&#39;s write our first function.
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Create a file called &lt;code&gt;test.hs&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;C-c C-z&lt;/code&gt; to get to the Haskell REPL, supplied by &lt;a href=&quot;http://www.haskell.org/haskellwiki/Haskell_mode_for_Emacs#inf-haskell.el:_the_best_thing_since_the_breadknife&quot;&gt;inf-haskell mode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Now, add the following to &lt;code&gt;test.hs&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-haskell&quot;&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;module&lt;/span&gt; &lt;span style=&quot;color: #228b22;&quot;&gt;Examples&lt;/span&gt; &lt;span style=&quot;color: #a020f0;&quot;&gt;where&lt;/span&gt;

&lt;span style=&quot;color: #0000ff;&quot;&gt;square&lt;/span&gt; &lt;span style=&quot;color: #a0522d;&quot;&gt;::&lt;/span&gt; &lt;span style=&quot;color: #228b22;&quot;&gt;Integral&lt;/span&gt; a &lt;span style=&quot;color: #a0522d;&quot;&gt;=&amp;gt;&lt;/span&gt; a &lt;span style=&quot;color: #a0522d;&quot;&gt;-&amp;gt;&lt;/span&gt; a
&lt;span style=&quot;color: #0000ff;&quot;&gt;square&lt;/span&gt; x &lt;span style=&quot;color: #a0522d;&quot;&gt;=&lt;/span&gt; x &lt;span style=&quot;color: #a0522d;&quot;&gt;*&lt;/span&gt; x
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Then type &lt;code&gt;C-c C-l&lt;/code&gt; in that buffer to load the file&#39;s contents into the REPL. &lt;code&gt;C-c C-z&lt;/code&gt; over to the repl again and try it out:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-haskell&quot;&gt;&lt;span style=&quot;color: #a0522d;&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color: #228b22;&quot;&gt;Main&lt;/span&gt;&lt;span style=&quot;color: #a0522d;&quot;&gt;&amp;gt;&lt;/span&gt; square 10
100
&lt;span style=&quot;color: #a0522d;&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color: #228b22;&quot;&gt;Main&lt;/span&gt;&lt;span style=&quot;color: #a0522d;&quot;&gt;&amp;gt;&lt;/span&gt; square 34
1156
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Nice.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;Flymake&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
Next, we&#39;re going to add support for Flymake, the emacs syntax-checker. This is a piece of Emacs functionality that&#39;ll run our Haskell files through the compiler every few seconds and associate any warnings and errors with some specific line in our Haskell file.
&lt;/p&gt;

&lt;p&gt;
While we&#39;re at it, we&#39;ll configure &lt;code&gt;hs-lint&lt;/code&gt;, which we can run periodically on our Haskell files for a list of hints as to how to structure code better. For example, running &lt;code&gt;hs-lint&lt;/code&gt; on a Haskell file containing &lt;code&gt;times2 x = (*) 2 x&lt;/code&gt; yields a buffer with:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-text&quot;&gt;hlint /Users/sritchie/test.hs
/Users/sritchie/test.hs:1:1: Error: Eta reduce
Found:
  times2 x = (*) 2 x
Why not:
  times2 = (*) 2

 1 suggestion
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Definitely helpful in the learning process.
&lt;/p&gt;

&lt;p&gt;
Flymake depends on an external perl script, &lt;code&gt;hslint&lt;/code&gt;, to pass the contents of the current buffer into the Haskell compiler.
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Download &lt;code&gt;hslint&lt;/code&gt; from &lt;a href=&quot;https://gist.github.com/1241073&quot;&gt;this gist&lt;/a&gt; and place it somewhere on your path.
&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;chmod a+x hslint&lt;/code&gt; to give the file executable privileges
&lt;/li&gt;
&lt;li&gt;Add the following to &lt;code&gt;.emacs&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;flymake-haskell-init&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;()&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;When flymake triggers, generates a tempfile containing the&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  contents of the current buffer, runs `hslint` on it, and&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  deletes file. Put this file path (and run `chmod a+x hslint`)&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  to enable hslint: https://gist.github.com/1241073&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;let*&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;((&lt;/span&gt;temp-file   &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;flymake-init-create-temp-buffer-copy
                       &#39;flymake-create-temp-inplace&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;local-file  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;file-relative-name
                       temp-file
                       &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;file-name-directory buffer-file-name&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;list &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;hslint&quot;&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;list local-file&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;flymake-haskell-enable&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;()&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;Enables flymake-mode for haskell, and sets &amp;lt;C-c d&amp;gt; as command&lt;/span&gt;
&lt;span style=&quot;color: #8b2252;&quot;&gt;  to show current error.&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;when&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;and buffer-file-name
             &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;file-writable-p
              &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;file-name-directory buffer-file-name&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
             &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;file-writable-p buffer-file-name&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;local-set-key &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;kbd &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;C-c d&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &#39;flymake-display-err-menu-for-current-line&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;flymake-mode t&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;Forces flymake to underline bad lines, instead of fully&lt;/span&gt;
&lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;highlighting them; remove this if you prefer full highlighting.&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;custom-set-faces
 &#39;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;flymake-errline &lt;span style=&quot;color: #8c8c8c;&quot;&gt;((((&lt;/span&gt;class color&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;:underline&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;red&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))))&lt;/span&gt;
 &#39;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;flymake-warnline &lt;span style=&quot;color: #8c8c8c;&quot;&gt;((((&lt;/span&gt;class color&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;:underline&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;yellow&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-5&quot;&gt;Haskell Extensions&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-5&quot;&gt;
&lt;/div&gt;&lt;div id=&quot;outline-container-sec-1-5-1&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-5-1&quot;&gt;Auto Complete Mode&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-5-1&quot;&gt;
&lt;p&gt;
Now, let&#39;s add autocompletion. Autocomplete mode is awesome; it provides IDE-like word tab completion of words based on info in open buffers, and some knowledge of the modes of the emacs buffer you&#39;re currently working in. In Haskell, we&#39;ll get autocompletion of every function we define, plus help with core language constructs. Head over to &lt;a href=&quot;http://cx4a.org/software/auto-complete/index.html&quot;&gt;Auto Complete Mode&lt;/a&gt; to download the package, and install with the following:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download and unpack Autocomplete mode
&lt;/li&gt;
&lt;li&gt;Open emacs, and run &lt;code&gt;M-x load-file&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Point the minibuffer to &lt;code&gt;&amp;lt;autocomplete-root&amp;gt;/etc/install.el&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Follow the remaining &lt;a href=&quot;http://cx4a.org/software/auto-complete/manual.html#Installation&quot;&gt;AC install instructions&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
That should get you all set for the next step.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-5-2&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;sec-1-5-2&quot;&gt;Linting!&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-1-5-2&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download &lt;a href=&quot;https://gist.github.com/1241059&quot;&gt;hs-lint.el&lt;/a&gt; and &lt;a href=&quot;https://gist.github.com/1241063&quot;&gt;haskell-ac.el&lt;/a&gt; and place each file inside of &lt;code&gt;~/.emacs.d&lt;/code&gt;. (&lt;code&gt;hs-lint&lt;/code&gt; is our linter, of course, and &lt;code&gt;haskell-ac.el&lt;/code&gt; provides autocomplete mode with some knowledge of a few core Haskell constructs.
&lt;/li&gt;
&lt;li&gt;Add the following to your &lt;code&gt;.emacs&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;require&lt;/span&gt; &#39;&lt;span style=&quot;color: #008b8b;&quot;&gt;hs-lint&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;    &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;https://gist.github.com/1241059&lt;/span&gt;
&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;require&lt;/span&gt; &#39;&lt;span style=&quot;color: #008b8b;&quot;&gt;haskell-ac&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;https://gist.github.com/1241063&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;my-haskell-mode-hook&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;()&lt;/span&gt;
  &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;hs-lint binding, plus autocompletion and paredit.&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;local-set-key &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;\C-cl&quot;&lt;/span&gt; &#39;hs-lint&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;setq ac-sources
        &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;append &#39;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;ac-source-yasnippet
                  ac-source-abbrev
                  ac-source-words-in-buffer
                  my/ac-source-haskell&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                ac-sources&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
  &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;dolist&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;x &#39;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;haskell literate-haskell&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
    &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;add-hook
     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;intern &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;concat &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;symbol-name x&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
                     &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;-mode-hook&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
     &#39;turn-on-paredit&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;

&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;eval-after-load&lt;/span&gt; &#39;haskell-mode
  &#39;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;progn&lt;/span&gt;
     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;require&lt;/span&gt; &#39;&lt;span style=&quot;color: #008b8b;&quot;&gt;flymake&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;push &#39;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;\\.l?hs\\&#39;&quot;&lt;/span&gt; flymake-haskell-init&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt; flymake-allowed-file-name-masks&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;add-hook &#39;haskell-mode-hook &#39;flymake-haskell-enable&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
     &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;add-hook &#39;haskell-mode-hook &#39;my-haskell-mode-hook&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The above code binds &lt;code&gt;C-c l&lt;/code&gt; to &lt;code&gt;hs-lint&lt;/code&gt; inside of Haskell buffers, and configures a large number of Haskell keywords for autocompletion. Go and test it out inside of &lt;code&gt;test.hs&lt;/code&gt;; you should find that &lt;code&gt;square&lt;/code&gt; autocompletes when you begin typing.
&lt;/p&gt;

&lt;p&gt;
Now go ahead and add the following line to &lt;code&gt;test.hs&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-haskell&quot;&gt;&lt;span style=&quot;color: #0000ff;&quot;&gt;face&lt;/span&gt; &lt;span style=&quot;color: #a0522d;&quot;&gt;::&lt;/span&gt; &lt;span style=&quot;color: #228b22;&quot;&gt;Int&lt;/span&gt; &lt;span style=&quot;color: #a0522d;&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: #228b22;&quot;&gt;Bool&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Upon save, or within seconds, you should see an angry red underline. Move the cursor over that line and type &lt;code&gt;C-c d&lt;/code&gt;, and you&#39;ll see a tooltip with the following text:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-text&quot;&gt;1: The type signature for `face&#39; lacks an accompanying binding.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Adding this will clear things up:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-haskell&quot;&gt;&lt;span style=&quot;color: #0000ff;&quot;&gt;face&lt;/span&gt; x &lt;span style=&quot;color: #a0522d;&quot;&gt;=&lt;/span&gt; 5 &lt;span style=&quot;color: #a0522d;&quot;&gt;&amp;lt;&lt;/span&gt; x
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-6&quot;&gt;Finishing Up&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-6&quot;&gt;
&lt;p&gt;
I hope this helped those of you looking to get started exploring Haskell! Please let me know in the comments if anything could be clearer; I&#39;ll be posting more down the road, and all requests are welcome.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Simple Hadoop Clusters</title>
   <link href="http://sritchie.github.com/2011/05/31/hadoop-on-pallet/"/>
   <updated>2011-05-31T00:00:00+00:00</updated>
   <id>http://sritchie.github.com/2011/05/31/hadoop-on-pallet</id>
   <content type="html">&lt;p class=&quot;meta&quot;&gt;31 May 2011 - Washington, DC&lt;/p&gt;

&lt;div id=&quot;outline-container-sec-1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sec-1&quot;&gt;Introducing Pallet-Hadoop&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-1&quot;&gt;
&lt;p&gt;
I&#39;m excited to announce &lt;a href=&quot;https://github.com/pallet/pallet-hadoop&quot;&gt;Pallet-Hadoop&lt;/a&gt;, a configuration library written in Clojure for Apache&#39;s &lt;a href=&quot;http://hadoop.apache.org/&quot;&gt;Hadoop&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
In the tutorial, we&#39;re going to see how to create a three node Hadoop cluster on EC2, and run a word count on MapReduce. We&#39;ll be following along with &lt;a href=&quot;https://github.com/pallet/pallet-hadoop-example&quot;&gt;Pallet-Hadoop example project&lt;/a&gt; for the introduction; for a more in-depth discussion of the design of pallet-hadoop, see the &lt;a href=&quot;https://github.com/pallet/pallet-hadoop/wiki&quot;&gt;project wiki&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;outline-container-sec-1-1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-1&quot;&gt;Background&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-1&quot;&gt;
&lt;p&gt;
Hadoop is an Apache java framework that allows for distributed processing of enormous datasets across large clusters. It combines a computation engine based on &lt;a href=&quot;http://en.wikipedia.org/wiki/MapReduce&quot;&gt;MapReduce&lt;/a&gt; with &lt;a href=&quot;http://hadoop.apache.org/hdfs/docs/current/hdfs_design.html&quot;&gt;HDFS&lt;/a&gt;, a distributed filesystem based on the &lt;a href=&quot;http://en.wikipedia.org/wiki/Google_File_System&quot;&gt;Google File System&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Abstraction layers such as &lt;a href=&quot;https://github.com/cwensel/cascading&quot;&gt;Cascading&lt;/a&gt; (for Java) and &lt;a href=&quot;https://github.com/nathanmarz/cascalog&quot;&gt;Cascalog&lt;/a&gt; (for &lt;a href=&quot;http://clojure.org/&quot;&gt;Clojure&lt;/a&gt;) make writing MapReduce queries quite nice. Indeed, running hadoop locally with cascalog &lt;a href=&quot;http://nathanmarz.com/blog/introducing-cascalog-a-clojure-based-query-language-for-hado.html&quot;&gt;couldn&#39;t be easier&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Unfortunately, graduating one&#39;s MapReduce jobs to the cluster level isn&#39;t so easy. Amazon&#39;s &lt;a href=&quot;http://aws.amazon.com/elasticmapreduce/&quot;&gt;Elastic MapReduce&lt;/a&gt; is a great option for getting up and running fast; but what to do if you want to configure your own cluster?
&lt;/p&gt;

&lt;p&gt;
After surveying existing tools, I decided to write my own layer over &lt;a href=&quot;https://github.com/pallet/pallet&quot;&gt;Pallet&lt;/a&gt;, a wonderful cloud provisioning library written in Clojure. Pallet runs on top of &lt;a href=&quot;https://github.com/jclouds/jclouds&quot;&gt;jclouds&lt;/a&gt;, which allows pallet to define its operations independent of any one cloud provider. Switching between clouds involves a change of login credentials, nothing more.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-2&quot;&gt;Setting Up&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-2&quot;&gt;
&lt;p&gt;
Before you get your first cluster running, you&#39;ll need to &lt;a href=&quot;https://aws-portal.amazon.com/gp/aws/developer/registration/index.html&quot;&gt;create an AWS account&lt;/a&gt;. Once you&#39;ve done this, navigate to &lt;a href=&quot;http://aws.amazon.com/account/&quot;&gt;your account page&lt;/a&gt; and follow the &quot;Security Credentials&quot; link. Under &quot;Access Credentials&quot;, you should see a tab called &quot;Access Keys&quot;. Note down your Access Key ID and Secret Access Key for future reference.
&lt;/p&gt;

&lt;p&gt;
I&#39;m going to assume that you have some basic knowledge of clojure, and know how to get a project running using &lt;a href=&quot;https://github.com/technomancy/leiningen&quot;&gt;leiningen&lt;/a&gt; or &lt;a href=&quot;https://github.com/ninjudd/cake&quot;&gt;cake&lt;/a&gt;. Go ahead and clone &lt;a href=&quot;https://github.com/pallet/pallet-hadoop-example&quot;&gt;the example project&lt;/a&gt; to follow along:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;$ git clone git://github.com/pallet/pallet-hadoop-example.git
$ cd pallet-hadoop-example
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;
Open up &lt;code&gt;./src/pallet-hadoop-example/core.clj&lt;/code&gt; with your favorite text editor. &lt;code&gt;example-cluster&lt;/code&gt; contains a data description of a full hadoop cluster with:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;One master node functioning as jobtracker and namenode
&lt;/li&gt;
&lt;li&gt;Two slave nodes (&lt;code&gt;(slave-group 2)&lt;/code&gt;), each acting as datanode and tasktracker.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Start a repl:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;$ lein repl
 &lt;span style=&quot;color: #a0522d;&quot;&gt;user&lt;/span&gt;=&amp;gt; (use &lt;span style=&quot;color: #8b2252;&quot;&gt;&#39;pallet-hadoop-example.core) (bootstrap)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-3&quot;&gt;Compute Service&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-3&quot;&gt;
&lt;p&gt;
Pallet abstracts away details about specific cloud providers through the idea of a &quot;compute service&quot;. The combination of our cluster definition and our compute service will be enough to get our cluster running. We define a compute service at our REPL like so:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;user=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;ec2-service&lt;/span&gt;
         &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;compute-service &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;aws-ec2&quot;&lt;/span&gt;
                          &lt;span style=&quot;color: #008b8b;&quot;&gt;:identity&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;ec2-access-key-id&quot;&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;Swap in your access key ID&lt;/span&gt;
                          &lt;span style=&quot;color: #008b8b;&quot;&gt;:credential&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;ec2-secret-access-key&quot;&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt; &lt;span style=&quot;color: #b22222;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;Swap in your secret key&lt;/span&gt;
#&#39;pallet-hadoop-example.core/ec2-service
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Alternatively, if you want to keep these out of your code base, save the following to &lt;code&gt;~/.pallet/config.clj&lt;/code&gt;:
&lt;/p&gt;


&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;defpallet&lt;/span&gt;
  &lt;span style=&quot;color: #0000ff;&quot;&gt;:services&lt;/span&gt; {&lt;span style=&quot;color: #008b8b;&quot;&gt;:aws&lt;/span&gt; {&lt;span style=&quot;color: #008b8b;&quot;&gt;:provider&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;aws-ec2&quot;&lt;/span&gt;
                   &lt;span style=&quot;color: #008b8b;&quot;&gt;:identity&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;your-ec2-access-key-id&quot;&lt;/span&gt;
                   &lt;span style=&quot;color: #008b8b;&quot;&gt;:credential&lt;/span&gt; &lt;span style=&quot;color: #8b2252;&quot;&gt;&quot;your-ec2-secret-access-key&quot;&lt;/span&gt;}}&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
and define &lt;code&gt;ec2-service&lt;/code&gt; with:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;user=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #a020f0;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #0000ff;&quot;&gt;ec2-service&lt;/span&gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;compute-service-from-config-file &lt;span style=&quot;color: #008b8b;&quot;&gt;:aws&lt;/span&gt;&lt;span style=&quot;color: #8c8c8c;&quot;&gt;))&lt;/span&gt;
#&#39;user/ec2-service
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-4&quot;&gt;Booting the Cluster&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-4&quot;&gt;
&lt;p&gt;
Now that we have our compute service and our cluster defined, booting the cluster is as simple as the following:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;user=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;create-cluster example-cluster ec2-service&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The logs you see flying by are Pallet&#39;s SSH communications with the nodes in the cluster. On node startup, Pallet uses your local SSH key to gain passwordless access to each node, and coordinates all configuration using streams of SSH commands.
&lt;/p&gt;

&lt;p&gt;
Once &lt;code&gt;create-cluster&lt;/code&gt; returns, we&#39;re done! We now have a fully configured, multi-node Hadoop cluster at our disposal.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-5&quot;&gt;Running Word Count&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-5&quot;&gt;
&lt;p&gt;
To test our new cluster, we&#39;re going log in and run a word counting MapReduce job on a number of books from &lt;a href=&quot;http://www.gutenberg.org/wiki/Main_Page&quot;&gt;Project Gutenberg&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Point your browser to the &lt;a href=&quot;https://console.aws.amazon.com/ec2/&quot;&gt;EC2 Console&lt;/a&gt;, log in, and click &quot;Instances&quot; on the left.
&lt;/p&gt;

&lt;p&gt;
You should see three nodes running; click on the node whose security group contains &quot;jobtracker&quot;, and scroll the lower pane down to retrieve the public DNS address for the node. It&#39;ll look something like &lt;code&gt;ec2-50-17-103-174.compute-1.amazonaws.com&lt;/code&gt;. I&#39;ll refer to this address as &lt;code&gt;jobtracker.com&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Point your browser to &lt;code&gt;jobtracker.com:50030&lt;/code&gt;, and you&#39;ll see the JobTracker web console. (Keep this open, as it will allow us to watch our MapReduce job in action.).=jobtracker.com:50070= points to the NameNode console, with information about HDFS.
&lt;/p&gt;

&lt;p&gt;
Next, we&#39;ll SSH into the jobtracker, and operate as the &lt;code&gt;hadoop&lt;/code&gt; user. Head to your terminal and run the following commands:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;$ ssh jobtracker.com &lt;span style=&quot;color: #b22222;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #b22222;&quot;&gt;insert actual address, enter yes to continue connecting&lt;/span&gt;
$ sudo su - hadoop
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-6&quot;&gt;Copy Data to HDFS&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-6&quot;&gt;
&lt;p&gt;
At this point, we&#39;re ready to begin following along with Michael Noll&#39;s excellent &lt;a href=&quot;http://goo.gl/aALr9&quot;&gt;Hadoop configuration tutorial&lt;/a&gt;. (I&#39;ll cover some of the same ground for clarity.)
&lt;/p&gt;

&lt;p&gt;
Our first step will be to collect a bunch of text to process. We start by downloading the following seven books to a temp directory:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gutenberg.org/cache/epub/20417/pg20417.txt&quot;&gt;The Outline of Science, Vol. 1 (of 4) by J. Arthur Thomson&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gutenberg.org/cache/epub/5000/pg5000.txt&quot;&gt;The Notebooks of Leonardo Da Vinci&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gutenberg.org/cache/epub/4300/pg4300.txt&quot;&gt;Ulysses by James Joyce&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gutenberg.org/cache/epub/132/pg132.txt&quot;&gt;The Art of War by 6th cent. B.C. Sunzi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gutenberg.org/cache/epub/1661/pg1661.txt&quot;&gt;The Adventures of Sherlock Holmes by Sir Arthur Conan Doyle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gutenberg.org/cache/epub/972/pg972.txt&quot;&gt;The Devil’s Dictionary by Ambrose Bierce&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gutenberg.org/cache/epub/19699/pg19699.txt&quot;&gt;Encyclopaedia Britannica, 11th Edition, Volume 4, Part 3&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Running the following commands at the remote shell should do the trick.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;$ mkdir /tmp/books
$ cd /tmp/books
$ curl -O http://www.gutenberg.org/cache/epub/20417/pg20417.txt
$ curl -O http://www.gutenberg.org/cache/epub/5000/pg5000.txt
$ curl -O http://www.gutenberg.org/cache/epub/4300/pg4300.txt
$ curl -O http://www.gutenberg.org/cache/epub/132/pg132.txt
$ curl -O http://www.gutenberg.org/cache/epub/1661/pg1661.txt
$ curl -O http://www.gutenberg.org/cache/epub/972/pg972.txt
$ curl -O http://www.gutenberg.org/cache/epub/19699/pg19699.txt
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Next, navigate to the Hadoop directory:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;$ cd /usr/local/hadoop-0.20.2/
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And copy the books over to the distributed filesystem:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;/usr/local/hadoop-0.20.2$ hadoop dfs -copyFromLocal /tmp/books books
/usr/local/hadoop-0.20.2$ hadoop dfs -ls
Found 1 items
drwxr-xr-x   - hadoop supergroup          0 2011-06-01 06:12:21 /user/hadoop/books
/usr/local/hadoop-0.20.2$
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-7&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-7&quot;&gt;Running MapReduce&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-7&quot;&gt;
&lt;p&gt;
We&#39;re ready to run the MapReduce job. &lt;code&gt;wordcount&lt;/code&gt; takes an input path within HDFS, processes all items within, and saves the output to HDFS &amp;#x2013; to &lt;code&gt;books-output&lt;/code&gt;, in this case. Run this command:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;/usr/local/hadoop-0.20.2$ hadoop jar hadoop-examples-0.20.2-cdh3u0.jar wordcount books/ books-output/
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And you should see something very similar to this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;11/06/01 06:14:30 INFO input.FileInputFormat: Total input paths to process : 7
11/06/01 06:14:30 INFO mapred.JobClient: Running job: job_201106010554_0002
11/06/01 06:14:31 INFO mapred.JobClient:  map 0% reduce 0%
11/06/01 06:14:44 INFO mapred.JobClient:  map 57% reduce 0%
11/06/01 06:14:45 INFO mapred.JobClient:  map 71% reduce 0%
11/06/01 06:14:46 INFO mapred.JobClient:  map 85% reduce 0%
11/06/01 06:14:48 INFO mapred.JobClient:  map 100% reduce 0%
11/06/01 06:14:57 INFO mapred.JobClient:  map 100% reduce 33%
11/06/01 06:15:00 INFO mapred.JobClient:  map 100% reduce 66%
11/06/01 06:15:01 INFO mapred.JobClient:  map 100% reduce 100%
11/06/01 06:15:02 INFO mapred.JobClient: Job complete: job_201106010554_0002
11/06/01 06:15:02 INFO mapred.JobClient: Counters: 22
11/06/01 06:15:02 INFO mapred.JobClient:   Job Counters
11/06/01 06:15:02 INFO mapred.JobClient:     Launched reduce &lt;span style=&quot;color: #a0522d;&quot;&gt;tasks&lt;/span&gt;=3
11/06/01 06:15:02 INFO mapred.JobClient:     &lt;span style=&quot;color: #a0522d;&quot;&gt;SLOTS_MILLIS_MAPS&lt;/span&gt;=74992
11/06/01 06:15:02 INFO mapred.JobClient:     Total time spent by all reduces waiting after reserving slots (ms)=0
11/06/01 06:15:02 INFO mapred.JobClient:     Total time spent by all maps waiting after reserving slots (ms)=0
11/06/01 06:15:02 INFO mapred.JobClient:     Launched map &lt;span style=&quot;color: #a0522d;&quot;&gt;tasks&lt;/span&gt;=7
11/06/01 06:15:02 INFO mapred.JobClient:     Data-local map &lt;span style=&quot;color: #a0522d;&quot;&gt;tasks&lt;/span&gt;=7
11/06/01 06:15:02 INFO mapred.JobClient:     &lt;span style=&quot;color: #a0522d;&quot;&gt;SLOTS_MILLIS_REDUCES&lt;/span&gt;=46600
11/06/01 06:15:02 INFO mapred.JobClient:   FileSystemCounters
11/06/01 06:15:02 INFO mapred.JobClient:     &lt;span style=&quot;color: #a0522d;&quot;&gt;FILE_BYTES_READ&lt;/span&gt;=1610042
11/06/01 06:15:02 INFO mapred.JobClient:     &lt;span style=&quot;color: #a0522d;&quot;&gt;HDFS_BYTES_READ&lt;/span&gt;=6557336
11/06/01 06:15:02 INFO mapred.JobClient:     &lt;span style=&quot;color: #a0522d;&quot;&gt;FILE_BYTES_WRITTEN&lt;/span&gt;=2753014
11/06/01 06:15:02 INFO mapred.JobClient:     &lt;span style=&quot;color: #a0522d;&quot;&gt;HDFS_BYTES_WRITTEN&lt;/span&gt;=1334919
11/06/01 06:15:02 INFO mapred.JobClient:   Map-Reduce Framework
11/06/01 06:15:02 INFO mapred.JobClient:     Reduce input &lt;span style=&quot;color: #a0522d;&quot;&gt;groups&lt;/span&gt;=121791
11/06/01 06:15:02 INFO mapred.JobClient:     Combine output &lt;span style=&quot;color: #a0522d;&quot;&gt;records&lt;/span&gt;=183601
11/06/01 06:15:02 INFO mapred.JobClient:     Map input &lt;span style=&quot;color: #a0522d;&quot;&gt;records&lt;/span&gt;=127602
11/06/01 06:15:02 INFO mapred.JobClient:     Reduce shuffle &lt;span style=&quot;color: #a0522d;&quot;&gt;bytes&lt;/span&gt;=958780
11/06/01 06:15:02 INFO mapred.JobClient:     Reduce output &lt;span style=&quot;color: #a0522d;&quot;&gt;records&lt;/span&gt;=121791
11/06/01 06:15:02 INFO mapred.JobClient:     Spilled &lt;span style=&quot;color: #a0522d;&quot;&gt;Records&lt;/span&gt;=473035
11/06/01 06:15:02 INFO mapred.JobClient:     Map output &lt;span style=&quot;color: #a0522d;&quot;&gt;bytes&lt;/span&gt;=10812590
11/06/01 06:15:02 INFO mapred.JobClient:     Combine input &lt;span style=&quot;color: #a0522d;&quot;&gt;records&lt;/span&gt;=1111905
11/06/01 06:15:02 INFO mapred.JobClient:     Map output &lt;span style=&quot;color: #a0522d;&quot;&gt;records&lt;/span&gt;=1111905
11/06/01 06:15:02 INFO mapred.JobClient:     &lt;span style=&quot;color: #a0522d;&quot;&gt;SPLIT_RAW_BYTES&lt;/span&gt;=931
11/06/01 06:15:02 INFO mapred.JobClient:     Reduce input &lt;span style=&quot;color: #a0522d;&quot;&gt;records&lt;/span&gt;=183601
/usr/local/hadoop-0.20.2$
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-8&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-8&quot;&gt;Retrieving Output&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-8&quot;&gt;
&lt;p&gt;
Now that the MapReduce job has completed successfully, all that remains is to extract the results from HDFS and take a look.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-sh&quot;&gt;$ mkdir /tmp/books-output
$ hadoop dfs -getmerge books-output /tmp/books-output
$ head /tmp/books-output/books-output
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
You should see something very close to:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-text&quot;&gt;&quot;&#39;Ah!&#39;  2
&quot;&#39;Ample.&#39;       1
&quot;&#39;At    1
&quot;&#39;But,  1
&quot;&#39;But,&#39; 1
&quot;&#39;Come! 1
&quot;&#39;December      1
&quot;&#39;For   1
&quot;&#39;Hampshire.    1
&quot;&#39;Have  1
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Success!
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-9&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-9&quot;&gt;Killing the Cluster&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-9&quot;&gt;
&lt;p&gt;
When we&#39;re finished, we can kill our cluster with this command, back at the REPL:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;

&lt;pre class=&quot;src src-clojure&quot;&gt;user=&amp;gt; &lt;span style=&quot;color: #8c8c8c;&quot;&gt;(&lt;/span&gt;destroy-cluster example-cluster ec2-service&lt;span style=&quot;color: #8c8c8c;&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sec-1-10&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sec-1-10&quot;&gt;Next Installment&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-1-10&quot;&gt;
&lt;p&gt;
That&#39;s it for now! Next, we&#39;ll talk about how to test hadoop clusters using pallet-hadoop with &lt;a href=&quot;https://github.com/tbatchelli/vmfest&quot;&gt;vmfest&lt;/a&gt; to create a virtual machine cluster identical to your production cluster on the cloud.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 
</feed>
