distillery 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  Distillery extracts the "content" portion out of an HTML document. It applies heuristics based on element type, location, class/id name and other attributes to try and find the content part of the HTML document and return it.
4
4
 
5
- The logic for Distillery was heavily influenced by [Readability](https://www.readability.com/), who was nice enough to make [their logic](http://code.google.com/p/arc90labs-readability/source/browse/trunk/js/readability.js) open source. Distillery does *not* aim to be a direct port of that logic. See [iterationlabs/ruby-readability](https://github.com/iterationlabs/ruby-readability) for something closer to that.
5
+ The logic for Distillery was heavily influenced by [Readability](https://www.readability.com/), who was nice enough to make [their logic](http://code.google.com/p/arc90labs-readability/source/browse/trunk/js/readability.js) open source. Readability and Distillery share nearly the same logic for locating the content HTML element on the page, however Distillery does *not* aim to be a direct port of that logic (see [iterationlabs/ruby-readability](https://github.com/iterationlabs/ruby-readability) for that).
6
6
 
7
- Readability and Distillery share nearly the same logic for locating the content HTML element on the page. Readability, however, also aggressively cleans and transforms the content element HTML to be used for display in a reading environment. Distillery aims to clean slightly less aggressively, and allow the user of the gem to choose how (and if) they would like to clean content element HTML.
7
+ Readability and Distillery differ in how they clean and return the found page content. Readability is focused on stripping the page content down to just paragraphs of text for distraction-free reading, and thus aggressively cleans and transforms the content element HTML. Mostly, this is the conversion of some `<div>` elements and newlines to `<p>` elements. Distillery does no transformation of the content element, and instead returns the content as originally seen in the HTML document.
8
8
 
9
9
  ## Installation
10
10
 
@@ -25,10 +25,12 @@ Then you simply call `#distill!` on the document object to distill it and return
25
25
 
26
26
  doc.distill!
27
27
  > "distilled content"
28
+
29
+ ### Cleaning of the content
28
30
 
29
- Both the `Distill::Document#distill!` and `Distillery.distill` methods by default will clean the HTML of the content to remove elements from it which are unlikely to be the actual content. Usually, this is things like social media share buttons, widgets, advertisements, etc. If you would like to not clean the content, simply pass `:dirty => true` to either method:
31
+ Both the `Distill::Document#distill!` and `Distillery.distill` methods by default will clean the HTML of the content to remove elements from it which are unlikely to be the actual content. Usually, this is things like social media share buttons, widgets, advertisements, etc. If you would like to not clean the content, simply pass `:clean => false` to either method:
30
32
 
31
- doc.distill!(:dirty => true)
33
+ doc.distill!(:clean => false)
32
34
  > "raw distilled content"
33
35
 
34
36
  ## From the command line
data/Rakefile CHANGED
@@ -19,7 +19,7 @@ namespace :fixture do
19
19
  task :score, :filename do |t, args|
20
20
  doc = doc_for_fixture(args[:filename])
21
21
 
22
- doc.prep_for_distillation
22
+ doc.score!
23
23
  doc.scores.each do |xpath, score|
24
24
  doc.at(xpath)['data-score'] = score.to_s
25
25
  end
@@ -58,24 +58,27 @@ module Distillery
58
58
  end
59
59
  end
60
60
 
61
- # Corrects improper use of HTML tags by coerceing elements that are likely paragraphs
62
- # to <p> tags
63
- def coerce_elements_to_paragraphs!
64
- search('div').each do |div|
65
- div.name = "p" if has_no_block_children?(div) || has_only_empty_div_children?(div)
61
+ # Marks elements that are suitable for scoring with a special HTML attribute
62
+ def mark_scorable_elements!
63
+ search('div', 'p').each do |element|
64
+ if element.name == 'p' || scorable_div?(element)
65
+ element['data-distillery'] = 'scorable'
66
+ end
66
67
  end
67
68
  end
68
69
 
69
70
  # Scores the document elements based on an algorithm to find elements which hold page
70
71
  # content.
71
72
  def score!
72
- search('p').each do |paragraph|
73
+ mark_scorable_elements!
74
+
75
+ scorable_elements.each do |element|
73
76
  points = 1
74
- points += paragraph.text.split(',').length
75
- points += [paragraph.text.length / 100, 3].min
77
+ points += element.text.split(',').length
78
+ points += [element.text.length / 100, 3].min
76
79
 
77
- scores[paragraph.path] = points
78
- parent = paragraph.parent
80
+ scores[element.path] = points
81
+ parent = element.parent
79
82
  scores[parent.path] += points
80
83
  scores[parent.parent.path] += points.to_f/2
81
84
  end
@@ -90,22 +93,24 @@ module Distillery
90
93
  def distill!(options = {})
91
94
  prep_for_distillation!
92
95
  score!
93
- clean_top_scoring_element! unless options.delete(:clean) == false
96
+ clean_top_scoring_elements! unless options.delete(:clean) == false
94
97
 
95
- top_scoring_element.inner_html
98
+ top_scoring_elements.map(&:inner_html).join("\n")
96
99
  end
97
100
 
98
101
  # Attempts to clean the top scoring node from non-page content items, such as
99
102
  # advertisements, widgets, etc
100
- def clean_top_scoring_element!
101
- top_scoring_element.search("*").each do |node|
102
- node.remove if has_empty_text?(node)
103
- end
103
+ def clean_top_scoring_elements!
104
+ top_scoring_elements.each do |element|
105
+ element.search("*").each do |node|
106
+ node.remove if has_empty_text?(node)
107
+ end
104
108
 
105
- top_scoring_element.search("*").each do |node|
106
- if UNRELATED_ELEMENTS.include?(node.name) ||
107
- (node.text.count(',') < 2 && unlikely_to_be_content?(node))
108
- node.remove
109
+ element.search("*").each do |node|
110
+ if UNRELATED_ELEMENTS.include?(node.name) ||
111
+ (node.text.count(',') < 2 && unlikely_to_be_content?(node))
112
+ node.remove
113
+ end
109
114
  end
110
115
  end
111
116
  end
@@ -115,11 +120,14 @@ module Distillery
115
120
  def prep_for_distillation!
116
121
  remove_irrelevant_elements!
117
122
  remove_unlikely_elements!
118
- coerce_elements_to_paragraphs!
119
123
  end
120
124
 
121
125
  private
122
126
 
127
+ def scorable_elements
128
+ search('[data-distillery=scorable]')
129
+ end
130
+
123
131
  def augment_scores_by_link_weight!
124
132
  scores.each do |xpath, points|
125
133
  scores[xpath] = scores[xpath] * ( 1 - link_density(at(xpath)) )
@@ -132,10 +140,32 @@ module Distillery
132
140
  link_length.to_f / total_length.to_f
133
141
  end
134
142
 
135
- def top_scoring_element
136
- winner = scores.sort_by { |xpath, score| score }.reverse.first
143
+ def top_scoring_elements
144
+ # -score puts largest scores first, then by xpath to favor outter elements on tie
145
+ winner = scores.sort_by { |xpath, score| [-score, xpath] }.first
137
146
  top_xpath, top_score = winner || ['/html/body', 1]
138
- at(top_xpath)
147
+ top_element = at(top_xpath)
148
+
149
+ top_elements = [top_element]
150
+
151
+ top_element.parent.children.each do |sibling|
152
+ if scores[sibling.path] > top_score*0.25 && sibling.path != top_element.path
153
+ top_elements << sibling
154
+ end
155
+ end
156
+
157
+ top_elements.each do |element|
158
+ element.search('[data-distillery]').each do |element|
159
+ element.remove_attribute('data-distillery')
160
+ end
161
+ end
162
+
163
+ top_elements
164
+ end
165
+
166
+ def scorable_div?(elem)
167
+ elem.name == 'div' &&
168
+ (has_no_block_children?(elem) || has_only_empty_div_children?(elem))
139
169
  end
140
170
 
141
171
  def has_no_block_children?(elem)
@@ -1,3 +1,3 @@
1
1
  module Distillery
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -105,4 +105,14 @@ distillation_of 'ginger_cookies.html' do
105
105
  should_not =~ /Sponsored Links/ # Sidebar
106
106
  should_not =~ /User Reviews/ # Comments
107
107
  should_not =~ /Free Southern Food Newsletter!/ # Header
108
+ end
109
+
110
+ distillation_of 'bourbon_balls.html' do
111
+ should =~ /The Kentucky Derby will be run Saturday/
112
+ should =~ /Just drop one of your bourbon balls into a cup of coffee/
113
+ should =~ /You can also use the ganache as a cake frosting/
114
+
115
+ should_not =~ /I just tried the recipe forCellar Doo/ # Comments
116
+ should_not =~ /FIND A STATION/ # Header
117
+ should_not =~ /Car Talk/ # Footer
108
118
  end
Binary file
@@ -0,0 +1,950 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml"><head><title>Bourbon Balls Give A Sweet Kick To Kentucky Derby : NPR</title><meta http-equiv="Content-type" content="text/html; charset=UTF-8"></meta><meta http-equiv="Content-Language" content="en-us"></meta><meta name="robots" content="noarchive,index,follow"></meta><meta name="Copyright" content="Copyright (c) 2011 NPR"></meta><meta http-equiv="imagetoolbar" content="no"></meta><meta name="MSSmartTagsPreventParsing" content="true"></meta><meta name="Rating" content="General"></meta><meta name="date" content="Fri, 06 May 2011 00:01:00 -0400"></meta><link rel="canonical" href="http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby"></link><meta name="description" content="When the Kentucky Derby is run Saturday, many spectators will be sipping on Mint Juleps — and munching on bourbon balls, the sweet treats with a little kick. They're good business for chocolatier Erika Chavez-Graziano, who sells around 800 pounds of bourbon balls at Derby time."></meta><link rel="alternate" type="application/rss+xml" title="Recipes" href="/rss/rss.php?id=1139"></link><link type="text/css" rel="stylesheet" media="screen, print" href="/templates/css/generated/newsStory.css"></link><link type="text/css" rel="stylesheet" media="print" href="/templates/css/news/print_story.css"></link><script type="text/javascript" src="/templates/javascript/generated/newsStory.js">
4
+ </script>
5
+ <script type="text/javascript" src="http://community.npr.org/ver1.0/Direct/DirectProxy">
6
+ </script>
7
+ <script type="text/javascript" src="http://community.npr.org/ver1.0/Direct/FacebookProxy">
8
+ </script>
9
+ <script type="text/javascript">
10
+ document.domain="npr.org";
11
+ AddNamespace("NPR.community");
12
+ NPR.community.sitelifeServerURL = "http://community.npr.org/ver1.0/Direct/Process";
13
+ </script>
14
+ <!-- DFP TARGET -->
15
+ <script type="text/javascript" language="javascript">
16
+ <!--
17
+ var DFP = {};
18
+ DFP.tile = 0;
19
+ DFP.ord = Number(window.ord) || Math.floor(Math.random() * 1E10);
20
+
21
+ DFP.target = 'http://ad.doubleclick.net/adj/n6735.NPR/arts___life_food_recipes;program=morning_edition;theme=1139;storyid=136021711';
22
+ // -->
23
+ </script>
24
+
25
+
26
+ <!--[if IE]>
27
+ <style type="text/css" media="screen, print">
28
+ @import "/templates/css/news/ie.css";
29
+ </style>
30
+ <![endif]-->
31
+
32
+
33
+ <!-- DFP AD MANAGER -->
34
+ <script type="text/javascript" language="javascript">
35
+ <!--
36
+ DFP[0] = {"utype":null,"sz":"300x250","position":"right_main"};
37
+ DFP[1] = {"utype":null,"sz":"88x31","position":"search_box"};
38
+
39
+
40
+ DFP.renderLocation = function(location) {
41
+ var params = DFP[location];
42
+ var toRender = '<scr' + 'ipt src="'
43
+ + DFP.target
44
+ + ';sz=' + params.sz
45
+ + ';tile=' + ++DFP.tile
46
+ + ';sc=1'
47
+ + ';ord=' + DFP.ord
48
+ + ';" type="text/javascript" language="javascript"></scr' + 'ipt>';
49
+ document.write(toRender);
50
+ }
51
+ // -->
52
+ </script></head><body id="news" class="tmplNewsStory type1 id136021711 theme1139"> <!--googleoff: index--><div id="sectionWrap" class="artsPage">
53
+ <script type="text/javascript">
54
+ //<![CDATA[
55
+
56
+ function getDateStr()
57
+ {
58
+ var today = new Date();
59
+ var year = today.getYear();
60
+ if(year<1000) year+=1900;
61
+ var todayStr = toFullMonth(today.getMonth()) + " " + today.getDate() + ", " + year;
62
+ return todayStr;
63
+ }
64
+
65
+ //]]>
66
+ </script>
67
+
68
+ <div id="header">
69
+
70
+ <div id="login">
71
+ <div id="todaysdate">
72
+ <script type="text/javascript">
73
+ //<![CDATA[
74
+ document.write(getDateStr());
75
+ //]]>
76
+ </script>
77
+ </div>
78
+
79
+ <div class="loginwrap">
80
+ <div class="donate"><a href="/stations/donate/index.php?ps=st273"></a></div>
81
+ <p id="loginPElm">
82
+ <!--<a href="/stations/donate/?ps=sttn" class="donate">Donate</a> | -->
83
+ <a href="http://shop.npr.org/?utm_source=topnav&amp;utm_medium=topnav&amp;utm_campaign=topnav" class="shop">NPR Shop</a> |
84
+ <a href="/templates/community/" class="comm">NPR Social Media</a> |
85
+ <a href="/templates/reg/login.php">Login</a> |
86
+ <a href="/templates/reg" class="reg">Register</a>
87
+ </p>
88
+ </div>
89
+
90
+ </div>
91
+
92
+ <div class="headercontent">
93
+ <a href="/"><img src="http://media.npr.org/chrome/news/nprlogo_138x46.gif" width="138" height="46" alt="NPR" /></a>
94
+
95
+ <div class="supportwrap">
96
+ <div class="support">
97
+
98
+ <div class="tab" id="globalstationlocation">
99
+ </div>
100
+ <script src="/templates/javascript/localize/station_include_global.js" type="text/javascript"></script>
101
+
102
+ <p><a href="/templates/stations/stations/" class="find">Find a Station</a></p>
103
+ </div>
104
+ </div>
105
+
106
+ <!--<div class="topnavSponsor">
107
+ <div class="searchSponsorTxt">Support<br />provided by:</div>
108
+ <div class="searchSponsor"><a href=""><img src="/banners/generic_88x31.gif" width="88" height="31" alt="" /></a></div>
109
+ </div>-->
110
+
111
+ <!-- DFP Search Sponsorship -->
112
+ <!-- COMMENTED IN - IN USE CURRENTLY -->
113
+ <div class="topnavSponsor">
114
+ <script language="javascript" type="text/javascript">
115
+ <!--
116
+ try {
117
+ DFP.renderLocation(1);
118
+ } catch(e){
119
+ NPR.messaging.warning('Problem fetching/rendering ad server code.','DFP.renderlocation',NPR.messaging.constants.SPONSORSHIP_ERROR);
120
+ }
121
+ // -->
122
+ </script>
123
+ </div>
124
+
125
+ <form action="/search/index.php" method="get" name="searchForm" id="searchForm" class="search">
126
+ <label for="searchinput">Search</label>
127
+ <input type="text" name="searchinput" id="searchinput" class="textbox searchbox"/>
128
+ <input type="button" value="" class="btn_go" onclick="submit();" />
129
+ </form>
130
+
131
+ </div>
132
+
133
+ <!-- localization dropdown -->
134
+ <div class="ddstn">
135
+
136
+ <!-- station info will be written here -->
137
+ <div id="station_info" class="station"></div>
138
+
139
+ <a href="javascript:void(0);" class="closemenu">close</a>
140
+
141
+ <div class="stationdata_arrow">&nbsp;</div>
142
+
143
+ </div>
144
+
145
+
146
+ <!-- global nav -->
147
+ <ul id="nav">
148
+ <li class="navhome"><a href="/">home</a></li>
149
+ <li class="navnews"><a href="/sections/news/">news</a></li>
150
+ <li class="navarts"><a href="/sections/arts/">arts &amp; life</a></li>
151
+ <li class="navmusic"><a href="/music/">music</a></li>
152
+ <li class="navprograms"><a href="javascript:void(0);">programs</a></li>
153
+ <li class="navlisten"><a href="javascript:void(0);">listen</a></li>
154
+ <li class="navhourly"><a href="javascript:getNewsCast();"></a></li>
155
+ <li class="navpodcasts"><a href="/podcasts/"></a></li>
156
+ </ul>
157
+
158
+ <!-- programs dropdown -->
159
+ <div id="menuprograms">
160
+
161
+ <div class="col news">
162
+ <h3>news</h3>
163
+ <ul class="left">
164
+ <li><a href="/programs/morning-edition/">Morning Edition</a></li>
165
+ <li><a href="/programs/all-things-considered/">All Things Considered</a></li>
166
+ <li><a href="/programs/fresh-air/">Fresh Air</a></li>
167
+ <li><a href="http://thedianerehmshow.org">The Diane Rehm Show</a></li>
168
+ <li><a href="http://www.onthemedia.org">On The Media</a></li>
169
+ <li><a href="http://www.onpointradio.org/">On Point</a></li>
170
+ <li><a href="/programs/talk-of-the-nation/">Talk of the Nation</a></li>
171
+ </ul>
172
+ <ul class="right">
173
+ <li><a href="http://sciencefriday.com/">Talk of the Nation Science Friday</a></li>
174
+ <li><a href="/programs/tell-me-more/">Tell Me More</a></li>
175
+ <li><a href="/programs/weekend-edition-saturday/">Weekend Edition Saturday</a></li>
176
+ <li><a href="/programs/weekend-edition-sunday/">Weekend Edition Sunday</a></li>
177
+ <li class="hdr">Also heard on NPR stations:</li>
178
+ <li class="other"><a href="http://marketplace.publicradio.org">Marketplace <span>APM</span></a></li>
179
+ </ul>
180
+ </div>
181
+
182
+ <div class="col entertainment">
183
+ <h3>arts &amp; life</h3>
184
+ <ul>
185
+ <li><a href="http://www.cartalk.com">Car Talk</a></li>
186
+ <li><a href="http://www.radiolab.org">Radiolab</a></li>
187
+ <li><a href="http://snapjudgment.org">Snap Judgment</a></li>
188
+ <li><a href="/programs/wait-wait-dont-tell-me/">Wait Wait...Don't Tell Me!</a></li>
189
+ <li class="hdr">Also heard on NPR stations:</li>
190
+ <li class="other"><a href="http://www.thisamericanlife.org">This American Life <span>PRI</span></a></li>
191
+ <li class="other"><a href="http://prairiehome.publicradio.org">A Prairie Home Companion <span>APM</span></a></li>
192
+ </ul>
193
+ </div>
194
+
195
+ <div class="col music">
196
+ <h3>music</h3>
197
+ <ul class="left">
198
+ <li><a href="/programs/all-songs-considered/">All Songs Considered</a></li>
199
+ <li><a href="http://www.fromthetop.org/">From The Top</a></li>
200
+ <li><a href="/programs/jazzset/">JazzSet</a></li>
201
+ <li><a href="/programs/piano-jazz/">Marian McPartland's Piano Jazz</a></li>
202
+ <li><a href="/series/mountain-stage/">Mountain Stage</a></li>
203
+ </ul>
204
+ <ul class="right">
205
+ <li><a href="/series/4703895/song-of-the-day">Song of the Day</a></li>
206
+ <li><a href="http://www.thistleradio.com/">The Thistle &amp; Shamrock</a></li>
207
+ <li><a href="/programs/world-cafe/">World Cafe</a></li>
208
+ <li><a href="/programs/world-of-opera/">World of Opera</a></li>
209
+ </ul>
210
+ </div>
211
+
212
+ <div class="col special">
213
+ <h3>special series</h3>
214
+ <ul>
215
+ <li><a href="/series/4516989/storycorps">StoryCorps</a></li>
216
+ <li><a href="/blogs/money/">Planet Money</a></li>
217
+ <li><a href="/blogs/pictureshow/">Picture Show</a></li>
218
+ <li><a href="/blogs/krulwich/">Krulwich Wonders...</a></li>
219
+ </ul>
220
+ </div>
221
+
222
+ <div class="footer">
223
+ <a href="/programs/">PUBLIC RADIO PROGRAMS A-Z </a> <span class="pipe">|</span> <a href="#" class="closemenu">close</a>
224
+ </div>
225
+
226
+
227
+ </div>
228
+ <!-- end programs dropdown -->
229
+
230
+
231
+ <!-- listen dropdown -->
232
+ <div id="menulisten">
233
+ <div class="audiostreams">
234
+ <h3>hear continuous streams</h3>
235
+ <div class="listenblock">
236
+ <div class="primary">
237
+ <a href="javascript:getProgramStream();" class="listenicon"></a>
238
+ <div class="listencontent">
239
+ <h4><a href="javascript:getProgramStream();">24-Hour Program Stream</a></h4>
240
+ <p class="info">NPR News and Shows</p>
241
+ </div>
242
+ </div>
243
+ <div class="tab"><span><a href="/audiohelp/progstream.html" class="trans">View Schedule</a></span></div>
244
+ <p class="findstreams"><a href="/templates/stations/stations/">Find Stations</a> | <a href="/templates/websites/musicstreams.php?t=10001">Music Streams</a></p>
245
+ </div>
246
+ </div>
247
+
248
+ <script type="text/javascript" src="/include/navigation/news/listenLatestProgram.js"></script>
249
+
250
+ <div class="hourlynews">
251
+ <h3>hear the latest news</h3>
252
+ <div class="listenblock">
253
+ <div class="primary">
254
+ <a href="javascript:getNewsCast();" class="listenicon"></a>
255
+ <div class="listencontent">
256
+ <h4><a href="javascript:getNewsCast();">Hourly News Summary</a></h4>
257
+ <div class="duration">[4 min 45 sec]</div>
258
+ <p class="info">Latest NPR Newscast</p>
259
+ </div>
260
+ </div>
261
+ </div>
262
+ </div>
263
+ <div class="footer">
264
+ <a href="/programs/">Public Radio Programs A-Z</a> <span class="pipe">|</span> <a href="#" class="closemenu">close</a>
265
+ </div>
266
+
267
+
268
+ </div>
269
+ <!-- listen dropdown -->
270
+
271
+ <div class="beta"></div>
272
+
273
+ </div>
274
+ <div id="wrapper">
275
+ <div id="pagespanglobal">
276
+ </div>
277
+
278
+ <!-- END ID="PAGESPANGLOBAL" -->
279
+ <div id="wrapper_main">
280
+ <div id="main_content">
281
+ <div id="content">
282
+ <div id="storybody">
283
+ <ul class="breadcrumb">
284
+ <li><a href="http://www.npr.org/sections/arts/">Arts & Life</a> <span class="arrow">&gt;</span></li>
285
+
286
+ <li><a href="http://www.npr.org/sections/food/">Food</a> <span class="arrow">&gt;</span></li>
287
+
288
+ <li class="last"><a href="http://www.npr.org/sections/recipes/">Recipes</a></li>
289
+
290
+ </ul>
291
+ <input type="hidden" id="title136021711" value="Bourbon Balls Give A Sweet Kick To Kentucky Derby"></input>
292
+ <input type="hidden" id="modelShortUrl136021711" value="http://n.pr/j6ium4"></input>
293
+ <input type="hidden" id="modelFullUrl136021711" value="http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby"></input>
294
+ <div class="storytoolswrap">
295
+ <ul class="storytools">
296
+ <li class="twitterTool"><a class="retweetStory story136021711 twitter" href="javascript:void(0);" onclick="javascript:NPR.socialMedia.shareOnTwitter(136021711);">Twitter</a></li>
297
+ <li class="facebookLi fbStory136021711"><a class="facebook fbStory136021711" href="javascript:void(0);" onclick="javascript:NPR.socialMedia.shareOnFacebook(136021711);">Facebook</a></li>
298
+ <li class="shareStoryTool"><a href="javascript:void(0);" class="share">Share</a><div class="sharepop">
299
+ <ul>
300
+ <li><a href="http://www.stumbleupon.com/submit?url=http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby&amp;title=Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby" class="stumble" target="_blank">Stumble Upon</a></li>
301
+ <li><a class="reddit" title="Post this story to reddit" href="http://reddit.com/submit?url=http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby&amp;title=Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby" target="_blank">Reddit</a></li>
302
+ <li><a href="http://www.linkedin.com/shareArticle?mini=true&url=http%3A%2F%2Fwww.npr.org%2F2011%2F05%2F06%2F136021711%2Fbourbon-balls-give-a-sweet-kick-to-kentucky-derby&amp;title=Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby&amp;summary=When+the+Kentucky+Derby+is+run+Saturday%2C+many+spectators+will+be+sipping+on+Mint+Juleps+%E2%80%94+and+munching+on+bourbon+balls%2C+the+sweet+treats+with+a+little+kick.+They%27re+good+business+for+chocolatier+Erika+Chavez-Graziano%2C+who+sells+around+800+pounds+of+bourbon+balls+at+Derby+time." class="linkedin" target="_blank">Linkedin</a></li>
303
+ <li><a href="http://digg.com/submit?url=http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby&amp;title=Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby&amp;bodytext=When+the+Kentucky+Derby+is+run+Saturday%2C+many+spectators+will+be+sipping+on+Mint+Juleps+%E2%80%94+and+munching+on+bourbon+balls%2C+the+sweet+treats+with+a+little+kick.+They%27re+good+business+for+chocolatier+Erika+Chavez-Graziano%2C+who+sells+around+800+pounds+of+bourbon+balls+at+Derby+time.&amp;media=MEDIA" class="digg" target="_blank">Digg</a></li>
304
+ </ul>
305
+ <p class="footer"><a href="http://help.npr.org/npr/consumer/kbdetail.asp?kbid=46">What is this?</a></p>
306
+ </div>
307
+ <div class="shareselected">
308
+ <a href="javascript:;">Share</a>
309
+ </div>
310
+
311
+ <!-- END CLASS="SHARESELECTED" -->
312
+ </li>
313
+ <li class="commentStoryTool"><a href="#commentBlock" class="comment">Comments <span id="commentCnt"></span></a></li>
314
+ <li class="recommendStoryTool"><span id="recommendationCntHref" ></span></li>
315
+ <li class="right"><a target="_self" href="/templates/email/emailAFriend.php?storyId=136021711" class="email"></a></li>
316
+ <li class="right"><a class="print" href="javascript: window.print(); void(1);"></a></li>
317
+ </ul>
318
+ <div class="spacer">
319
+ &nbsp;
320
+ </div>
321
+ </div>
322
+ <!--googleon: index--> <div class="storytitle">
323
+ <h1>Bourbon Balls Give A Sweet Kick To Kentucky Derby</h1>
324
+ <div id="storybyline" class="storylocation">
325
+ <div class="bucketwrap byline" id="res136021725">
326
+ <p class="byline">by <span>NPR Staff</span></p>
327
+ </div>
328
+
329
+ <!-- END CLASS="BUCKETWRAP BYLINE" ID="RES136021725" PREVIEWTITLE="BYLINES" -->
330
+ </div>
331
+
332
+ <!-- END ID="STORYBYLINE" CLASS="STORYLOCATION" -->
333
+ </div>
334
+
335
+ <!-- END CLASS="STORYTITLE" -->
336
+ <div id="storyspan02" class="storylocation">
337
+ <div id="res136045361" class="bucketwrap primary">
338
+ <p class="date">May 6, 2011</p> <div class="listenicon">
339
+ <a href="javascript:NPR.Player.openPlayer(136021711, 136045361, null, NPR.Player.Action.PLAY_NOW, NPR.Player.Type.STORY, '0')"></a>
340
+ </div>
341
+
342
+ <!-- END CLASS="LISTENICON" -->
343
+ <div class="avcontent listen">
344
+ <h3><a href="javascript:NPR.Player.openPlayer(136021711, 136045361, null, NPR.Player.Action.PLAY_NOW, NPR.Player.Type.STORY, '0')">Listen to the Story</a></h3>
345
+ <p class="byline"><a class="program" href="http://www.npr.org/programs/morning-edition/">Morning Edition</a></p> <div class="duration">
346
+ [3 min 36 sec]
347
+ </div>
348
+ </div>
349
+
350
+ <!-- END CLASS="AVCONTENT LISTEN" -->
351
+ <ul>
352
+ <li><a class="add" href="javascript:NPR.Player.openPlayer(136021711, 136045361, null, NPR.Player.Action.ADD_TO_PLAYLIST, NPR.Player.Type.STORY, '0')"><span>Add to Playlist</span></a></li>
353
+ <li><a class="download" href="http://pd.npr.org/anon.npr-mp3/npr/me/2011/05/20110506_me_15.mp3?dl=1"><span>Download</span></a></li>
354
+ <li><a class="trans" href="/templates/transcript/transcript.php?storyId=136021711"><span>Transcript</span></a></li>
355
+ </ul>
356
+ <div class="spacer">
357
+ &nbsp;
358
+ </div>
359
+ </div>
360
+
361
+ <!-- END ID="RES136045361" CLASS="BUCKETWRAP PRIMARY" -->
362
+ </div>
363
+
364
+ <!-- END ID="STORYSPAN02" CLASS="STORYLOCATION" -->
365
+ <div id="storytext" class="storylocation">
366
+ <div id="res136028874" class="bucketwrap photo300">
367
+ <img src="http://media.npr.org/assets/img/2011/05/05/4187963232_2da2d1d9c9_b.jpg?t=1304623991&amp;s=2" width="300" class="img300 enlarge" title="Bourbon balls are dipped in molten chocolate instead of being cooked, which allows them to retain the flavor, and punch, of their namesake liquor." alt="Bourbon balls are dipped in molten chocolate instead of being cooked, which allows them to retain the flavor, and punch, of their namesake liquor."></img> <div class="captionwrap enlarge">
368
+ <a class="enlargeicon" alt="Enlarge" title="Enlarge Image" href="javascript:void(0);"><span>Enlarge</span></a> <span class="creditwrap"><span class="rightsnotice">lisacericola via Flickr</span></span> <p>Bourbon balls are dipped in molten chocolate instead of being cooked, which allows them to retain the flavor, and punch, of their namesake liquor.</p>
369
+ </div>
370
+
371
+ <!-- END CLASS="CAPTIONWRAP ENLARGE" -->
372
+ <div class="enlarge_measure">
373
+ <img src="http://media.npr.org/assets/img/2011/05/05/4187963232_2da2d1d9c9_b.jpg?t=1304623991&amp;s=51" title="Bourbon balls are dipped in molten chocolate instead of being cooked, which allows them to retain the flavor, and punch, of their namesake liquor." alt="Bourbon balls are dipped in molten chocolate instead of being cooked, which allows them to retain the flavor, and punch, of their namesake liquor."></img>
374
+ </div>
375
+
376
+ <!-- END CLASS="ENLARGE_MEASURE" -->
377
+ <div class="enlarge_html">
378
+ <span class="creditwrap"><span class="rightsnotice">lisacericola via Flickr</span></span> <p class="caption">Bourbon balls are dipped in molten chocolate instead of being cooked, which allows them to retain the flavor, and punch, of their namesake liquor.</p>
379
+ </div>
380
+
381
+ <!-- END CLASS="ENLARGE_HTML" -->
382
+ </div>
383
+ <div class="container con2col btmbar" id="con136029472">
384
+ <h3 class="conheader">More Recipe Options</h3>
385
+ <div id="res136028824" class="bucketwrap inset2col internallink">
386
+ <div class="bucket img">
387
+ <a href="http://www.npr.org/templates/story/story.php?storyId=16818225" id="featuredStackSquareImage16818225"><img src="http://media.npr.org/kitchen/2007/12/bourbonandchocolate/bourbon_choco75.jpg?t=1247588012" class="img75" title="thumbnail" alt="thumbnail"></img></a> <div class="bucketblock">
388
+ <h3 class="slug"><a href="http://www.npr.org/series/kitchen-window/">Kitchen Window </a></h3>
389
+ <h3><a href="http://www.npr.org/templates/story/story.php?storyId=16818225"> Chocolate Helps the Bourbon Go Down</a></h3>
390
+ </div>
391
+
392
+ <!-- END CLASS="BUCKETBLOCK" -->
393
+ </div>
394
+
395
+ <!-- END CLASS="BUCKET IMG" -->
396
+ </div>
397
+
398
+ <!-- END ID="RES136028824" CLASS="BUCKETWRAP INSET2COL INTERNALLINK" -->
399
+ </div>
400
+
401
+ <!-- END CLASS="CONTAINER CON2COL BTMBAR" ID="CON136029472" PREVIEWTITLE="MORE RECIPE OPTIONS" -->
402
+ <div id="featuredCommentsMain136021711">
403
+ </div>
404
+
405
+ <!-- END ID="FEATUREDCOMMENTSMAIN136021711" -->
406
+ <div class="dateblock">
407
+ <div class="textsize">
408
+ text size <a class="normal" href="javascript: void();">A</a> <a class="big" href="javascript: void();">A</a> <a class="bigger" href="javascript: void();">A</a>
409
+ </div>
410
+
411
+ <!-- END CLASS="TEXTSIZE" -->
412
+ <span class="date">May 6, 2011</span>
413
+ </div>
414
+ <p>The Kentucky Derby will be run Saturday, and many spectators will be sipping on Mint Juleps — and munching on bourbon balls, the sweet treats that have a little kick.</p> <p>There are several styles of bourbon balls, says Louisville chocolatier Erika Chavez-Graziano. She describes her version as "a dark chocolate confection; it's a butter-cream center, very creamy, very sweet — and it packs a punch. It's pretty boozy, as bourbon balls go."</p> <p>And the spiked sweets are good business for Chavez-Graziano's company, Cellar Door Chocolates.</p> <p>"They're a huge part of my business, especially this time of year," she tells NPR's Linda Wertheimer. "Right now, we will sell about 800 pounds of bourbon balls, which is pretty big. It puts us at about 30,000 individual pieces."</p> <p>Since the balls aren't baked, the alcohol from the bourbon isn't cooked off, as it is in many other candies and desserts.</p> <p>"After eating one, you can smell the bourbon on you for a little while," says Chavez-Graziano.</p> <p>But that doesn't necessarily mean you should use a high-end bourbon in them.</p> <p>"I think that those single-batch bourbons are best consumed neat" instead of in a candy, she says. "You would lose the finer nuances of all those wonderful bourbons — you would lose them in the chocolate, and the sugar, and the butter."</p> <p>Chavez-Graziano uses 80-proof Evan Williams, which she describes as "not a sweet bourbon; it's robust. It's a little peaty, it's perfect when you complement it with sugar and chocolate."</p> <p>Asked what kind of drink or food to pair with a bourbon ball, she says that one thing comes to mind: the espresso machine at her shop in downtown Louisville.</p> <p>"Just drop one of your bourbon balls into a cup of coffee," Chavez-Graziano says, "and it's a lovely combination."</p>
415
+ </div>
416
+ <!--googleoff: index--> <div class="childstory">
417
+ <a name="136021414"></a> <div class="storytitle">
418
+ <h1>Recipe: Cellar Door Chocolates' Bourbon Truffles</h1>
419
+ <div id="storybyline" class="storylocation">
420
+ <div class="bucketwrap byline" id="res136021502">
421
+ <p class="byline">by <span>Erika Chavez-Graziano</span></p>
422
+ </div>
423
+
424
+ <!-- END CLASS="BUCKETWRAP BYLINE" ID="RES136021502" PREVIEWTITLE="BYLINES" -->
425
+ </div>
426
+
427
+ <!-- END ID="STORYBYLINE" CLASS="STORYLOCATION" -->
428
+ </div>
429
+
430
+ <!-- END CLASS="STORYTITLE" -->
431
+ <div id="storytext" class="storylocation">
432
+ <div id="res136054720" class="bucketwrap photo300">
433
+ <img src="http://media.npr.org/assets/img/2011/05/06/bourbon_truffles_wide.jpg?t=1304695814&amp;s=2" width="300" class="img300 enlarge" title="Bourbon truffles await a coating of chopped pecans, at Cellar Door Chocolates." alt="Bourbon truffles await a coating of chopped pecans, at Cellar Door Chocolates."></img> <div class="captionwrap enlarge">
434
+ <a class="enlargeicon" alt="Enlarge" title="Enlarge Image" href="javascript:void(0);"><span>Enlarge</span></a> <span class="creditwrap"><span class="rightsnotice">Erika Chavez-Graziano</span></span> <p>Bourbon truffles await a coating of chopped pecans, at Cellar Door Chocolates.</p>
435
+ </div>
436
+
437
+ <!-- END CLASS="CAPTIONWRAP ENLARGE" -->
438
+ <div class="enlarge_measure">
439
+ <img src="http://media.npr.org/assets/img/2011/05/06/bourbon_truffles_wide.jpg?t=1304695814" title="Bourbon truffles await a coating of chopped pecans, at Cellar Door Chocolates." alt="Bourbon truffles await a coating of chopped pecans, at Cellar Door Chocolates."></img>
440
+ </div>
441
+
442
+ <!-- END CLASS="ENLARGE_MEASURE" -->
443
+ <div class="enlarge_html">
444
+ <span class="creditwrap"><span class="rightsnotice">Erika Chavez-Graziano</span></span> <p class="caption">Bourbon truffles await a coating of chopped pecans, at Cellar Door Chocolates.</p>
445
+ </div>
446
+
447
+ <!-- END CLASS="ENLARGE_HTML" -->
448
+ </div>
449
+ <div id="featuredCommentsMain136021414">
450
+ </div>
451
+
452
+ <!-- END ID="FEATUREDCOMMENTSMAIN136021414" -->
453
+ <div class="dateblock">
454
+ </div>
455
+ <p><strong>Ingredients</strong></p> <p><strong></strong>1 pound dark chocolate (55-71%), chopped</p> <p>1/2 cup heavy whipping cream</p> <p>1/2 cup Evan Williams bourbon</p> <p>pinch of sea salt</p> <p>1 cup finely chopped pecans</p> <p><strong>Instructions</strong></p> <p>Bring heavy whipping cream to a boil. Remove from heat. Add chocolate; stir until smooth. Add bourbon; stir until well-incorporated. Add salt. Let the chocolate mixture (ganache) rest at room temperature until firm (about 4 hours).</p> <p>Using two spoons, portion out half ounce balls of ganache. Hand roll balls until smooth and then roll it in the pecans.</p> <p><em>Makes about 40 truffles.</em></p> <p>You can also use the ganache as a cake frosting — just pour over cake or cupcakes while the ganache is still warm. It also makes a wonderful coffee addition. Before you roll the ganache in the pecans, put a couple of the balls in your cup of coffee — stir and enjoy!</p> <p><em>Erika Chavez-Graziano is the proprietor of Cellar Door Chocolates in Louisville, Ky.</em></p>
456
+ </div>
457
+ </div>
458
+
459
+ <!-- END CLASS="CHILDSTORY" -->
460
+ <div class="spacer">
461
+ &nbsp;
462
+ </div>
463
+ </div>
464
+
465
+ <!-- END ID="STORYBODY" -->
466
+ <div class="storytoolswrap stbottom">
467
+ <ul class="storytools">
468
+ <li class="twitterTool"><a class="retweetStory story136021711 twitter" href="javascript:void(0);" onclick="javascript:NPR.socialMedia.shareOnTwitter(136021711);">Twitter</a></li>
469
+ <li class="facebookLi fbStory136021711"><a class="facebook fbStory136021711" href="javascript:void(0);" onclick="javascript:NPR.socialMedia.shareOnFacebook(136021711);">Facebook</a></li>
470
+ <li class="shareStoryTool"><a href="javascript:void(0);" class="share">Share</a><div class="sharepop">
471
+ <ul>
472
+ <li><a href="http://www.stumbleupon.com/submit?url=http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby&amp;title=Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby" class="stumble" target="_blank">Stumble Upon</a></li>
473
+ <li><a class="reddit" title="Post this story to reddit" href="http://reddit.com/submit?url=http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby&amp;title=Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby" target="_blank">Reddit</a></li>
474
+ <li><a href="http://www.linkedin.com/shareArticle?mini=true&url=http%3A%2F%2Fwww.npr.org%2F2011%2F05%2F06%2F136021711%2Fbourbon-balls-give-a-sweet-kick-to-kentucky-derby&amp;title=Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby&amp;summary=When+the+Kentucky+Derby+is+run+Saturday%2C+many+spectators+will+be+sipping+on+Mint+Juleps+%E2%80%94+and+munching+on+bourbon+balls%2C+the+sweet+treats+with+a+little+kick.+They%27re+good+business+for+chocolatier+Erika+Chavez-Graziano%2C+who+sells+around+800+pounds+of+bourbon+balls+at+Derby+time." class="linkedin" target="_blank">Linkedin</a></li>
475
+ <li><a href="http://digg.com/submit?url=http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby&amp;title=Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby&amp;bodytext=When+the+Kentucky+Derby+is+run+Saturday%2C+many+spectators+will+be+sipping+on+Mint+Juleps+%E2%80%94+and+munching+on+bourbon+balls%2C+the+sweet+treats+with+a+little+kick.+They%27re+good+business+for+chocolatier+Erika+Chavez-Graziano%2C+who+sells+around+800+pounds+of+bourbon+balls+at+Derby+time.&amp;media=MEDIA" class="digg" target="_blank">Digg</a></li>
476
+ </ul>
477
+ <p class="footer"><a href="http://help.npr.org/npr/consumer/kbdetail.asp?kbid=46">What is this?</a></p>
478
+ </div>
479
+ <div class="shareselected">
480
+ <a href="javascript:;">Share</a>
481
+ </div>
482
+
483
+ <!-- END CLASS="SHARESELECTED" -->
484
+ </li>
485
+ <li class="commentStoryTool"><a href="#commentBlock" class="comment">Comments <span id="commentCnt2"></span></a></li>
486
+ <li class="recommendStoryTool"><span id="recommendationCntHref2" ></span></li>
487
+ <li class="right"><a target="_self" href="/templates/email/emailAFriend.php?storyId=136021711" class="email"></a></li>
488
+ <li class="right"><a class="print" href="javascript: window.print(); void(1);"></a></li>
489
+ </ul>
490
+ <div class="spacer">
491
+ &nbsp;
492
+ </div>
493
+ </div>
494
+ <div id="storybottom">
495
+ <div class="colwrap">
496
+ <h3 class="hed"><a href="http://www.npr.org/sections/recipes/">More Recipes</a></h3>
497
+ <div class="divider2col">
498
+ <div class="right">
499
+ <div class="feed">
500
+ <h3>Podcast + RSS Feeds</h3>
501
+ <div class="bucket">
502
+ <h4><div class="feedhdrs">
503
+ <span>Podcast</span> <span>RSS</span>
504
+ </div>
505
+
506
+ <!-- END CLASS="FEEDHDRS" -->
507
+ </h4>
508
+ <ul>
509
+ <li><span class="title">Recipes</span><a href="javascript: void(0);" class="podicon"></a><a href="http://www.npr.org/rss/rss.php?id=1139" class="rssicon"></a><div class="podbox" style="display:none;">
510
+ <p>Subscribe to <span>Recipes</span> podcast via:</p> <p><a href="itpc://www.npr.org/templates/rss/podlayer.php?id=1139" class="subitunes"><img src="http://media.npr.org/images/podcasts/ui/icon_itunes.gif" width="72" height="28" alt="iTunes"></img></a><a href="zune://subscribe/?Recipes=http://www.npr.org/templates/rss/podlayer.php?id=1139" class="subzune"><img src="http://media.npr.org/images/podcasts/ui/icon_zune.gif" width="56" height="28" alt="Zune"></img></a></p> <p class="divider">Or use this URL:</p> <p><a class="podurllink" href="http://www.npr.org/templates/rss/podlayer.php?id=1139"><img src="http://media.npr.org/images/podcasts/ui/icon_pod.gif" width="21" height="21" alt="This podcast"></img></a><input type="text" class="podurl" value="http://www.npr.org/templates/rss/podlayer.php?id=1139"></input>
511
+ </p> <p class="closethis"><a href="#">close</a></p> <div class="spacer">
512
+ &nbsp;
513
+ </div>
514
+ </div>
515
+
516
+ <!-- END CLASS="PODBOX" STYLE="DISPLAY:NONE;" -->
517
+ <div class="spacer">
518
+ &nbsp;
519
+ </div>
520
+ </li>
521
+ <li><span class="title">Morning Edition</span><span class="blankpod">&ndash;</span><a href="http://www.npr.org/rss/rss.php?id=3" class="rssicon"></a><div class="spacer">
522
+ &nbsp;
523
+ </div>
524
+ </li>
525
+ </ul>
526
+ <div class="spacer">
527
+ &nbsp;
528
+ </div>
529
+ </div>
530
+
531
+ <!-- END CLASS="BUCKET" -->
532
+ <div class="bucketbottom">
533
+ </div>
534
+
535
+ <!-- END CLASS="BUCKETBOTTOM" -->
536
+ </div>
537
+
538
+ <!-- END CLASS="FEED" -->
539
+ <iframe id="nl_iframe" frameborder="0" scrolling="no" src="/templates/reg/newsletter-bucket.php?slice=Sell&amp;guid=4dc7530404e29" style="width:100%;height:275px;"></iframe>
540
+ </div>
541
+
542
+ <!-- END CLASS="RIGHT" -->
543
+ <div id="res" class="bucketwrap apiquery">
544
+
545
+ <div class="more_stories">
546
+ <div class="bucket img">
547
+ <a href="http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby"><img src="http://media.npr.org/assets/img/2011/05/05/4187963232_2da2d1d9c9_b_sq.jpg?t=1304623991&amp;s=11" alt="Chocolatier Erika Chavez-Graziano says she sells around 800 pounds of bourbon balls at Derby time."></a><div class="bucketblock">
548
+ <h4><a href="http://www.npr.org/sections/recipes/">Recipes</a></h4>
549
+ <h3><a href="http://www.npr.org/2011/05/06/136021711/bourbon-balls-give-a-sweet-kick-to-kentucky-derby">Bourbon Balls Give A Sweet Kick To Kentucky Derby</a></h3>
550
+ <p>Chocolatier Erika Chavez-Graziano says she sells around 800 pounds of bourbon balls at Derby time.</p>
551
+ </div>
552
+ </div>
553
+ <div class="bucket img">
554
+ <a href="http://www.npr.org/2011/05/04/135634688/say-it-with-flour-muffins-scones-waffles-for-mom"><img src="http://media.npr.org/assets/img/2011/04/22/mothersdaywaffles_sq.jpg?t=1303840879&amp;s=11" alt="Kids can make treats with easy prep and cleanup, no knives or flames, and spare time to make a card."></a><div class="bucketblock">
555
+ <h4><a href="http://www.npr.org/sections/recipes/">Kitchen Window</a></h4>
556
+ <h3><a href="http://www.npr.org/2011/05/04/135634688/say-it-with-flour-muffins-scones-waffles-for-mom">Say It With Flour: Muffins, Scones, Waffles For Mom</a></h3>
557
+ <p>Kids can make treats with easy prep and cleanup, no knives or flames, and spare time to make a card.</p>
558
+ </div>
559
+ </div>
560
+ <div class="bucket"><div class="bucketblock">
561
+ <h4><a href="http://www.npr.org/sections/recipes/">Recipes</a></h4>
562
+ <h3><a href="http://www.npr.org/2011/05/03/135709698/millet-muffins">Millet Muffins</a></h3>
563
+ <p>From the Kitchen Window column</p>
564
+ </div></div>
565
+ </div>
566
+
567
+ </div>
568
+
569
+ <!-- END ID="RES" CLASS="BUCKETWRAP APIQUERY" -->
570
+ <div class="spacer">
571
+ &nbsp;
572
+ </div>
573
+ </div>
574
+
575
+ <!-- END CLASS="DIVIDER2COL" -->
576
+ </div>
577
+
578
+ <!-- END CLASS="COLWRAP" -->
579
+ </div>
580
+
581
+ <!-- END ID="STORYBOTTOM" -->
582
+ <a name="commentBlock">&nbsp</a> <div class="commentbox">
583
+ <h3>Comments</h3>
584
+ <p></p> <p id="commentBoxLinkSuccess" style="display:none"></p> <p>Please keep your community civil. All comments must follow the NPR.org <a href="/discussionrules" target="_blank">Community rules</a> and <a href="/about/termsofuse.html" target="_blank">terms of use</a>. See also the <a href="/communityFAQ" target="_blank">Community FAQ</a>.</p> <div class="commentingerrors" style="display:none">
585
+ </div>
586
+ <p id="commentBoxNotLoggedIn" style="display:none"><span class="instruct">You must be logged in to leave a comment.</span> <a href="javascript: NPR.community.renderOverlay(NPR.community.LOGIN);">Login / Register</a></p> <p id="commentBoxNeedMoreInfo" style="display:none"><span class="instruct">More information is required for you to participate in the NPR online community.</span> <a href="javascript: NPR.community.renderOverlay(NPR.community.ADDITIONAL_INFO);">Add this information</a></p> <form><textarea cols="50" rows="4" name="commentBody" id="commentTextBox" maxlength="1250" disabled=""> </textarea><p class="charsRemaining"></p><input type="image" id="commentSubmit" src="http://media.npr.org/chrome/registration/bttn_submit.gif" onmouseover="this.src='http://media.npr.org/chrome/registration/bttn_submit_hover.gif'" onmouseout="this.src='http://media.npr.org/chrome/registration/bttn_submit.gif'" class="btn_submit" onClick="NPR.community.enterComment(136021711,'Bourbon Balls Give A Sweet Kick To Kentucky Derby',this.form.commentBody);return false;" disabled=""></input>
587
+ <div id="facebook_connect_wrapper" style="display: none;">
588
+ <input id="facebook_connect_checkbox" type="checkbox" name="facebook_connect_checkbox" disabled=""></input>
589
+ <label for="facebook_connect_checkbox">Post this comment to Facebook, too?</label>
590
+ </div>
591
+ </form> <div class="spacer">
592
+ &nbsp;
593
+ </div>
594
+ <div id="commentSubmissionFeedback" style="display:none;">
595
+ </div>
596
+
597
+ <!-- END ID="COMMENTSUBMISSIONFEEDBACK" STYLE="DISPLAY:NONE;" -->
598
+ <p>NPR reserves the right to read on the air and/or publish on its website or in any medium now known or unknown the e-mails and letters that we receive. We may edit them for clarity or brevity and identify authors by name and location. For additional information, please consult our <a href="/about/termsofuse.html">Terms of Use</a>.
599
+
600
+ </p> <div class="spacer">
601
+ &nbsp;
602
+ </div>
603
+ <span id="overlaysGoHere"></span>
604
+ </div>
605
+
606
+ <!-- END CLASS="COMMENTBOX" -->
607
+ <script type="text/javascript">
608
+ NPR.community.commentPageNum = 1;
609
+ NPR.community.pluckPageNum = 1;
610
+ NPR.community.pluckCommentKey = '';
611
+ NPR.community.numCommentToDisplay=10;
612
+ NPR.community.storyId = 136021711;
613
+ NPR.community.storyTitle = NPR.community.urlencode('Bourbon Balls Give A Sweet Kick To Kentucky Derby');
614
+ </script>
615
+ <form class="sortcomments" style="display:none" ><select id="sortCommentsSelector" onchange="NPR.community.sortComments(this);" onclick="NPR.community.sortComments(this);">
616
+ <option value="TimeStampDescending">Recent First</option>
617
+ <option value="TimeStampAscending">Oldest First</option>
618
+ <option value="RecommendationsDescending">Most Recommended</option>
619
+ </select>
620
+ </form> <div id="comments">
621
+ </div>
622
+
623
+ <!-- END ID="COMMENTS" -->
624
+ <p class="commentsarchive" style="display:none"><a href="/templates/story/storyComments.php?storyId=136021711">View all comments <span id="commentCntArchive"></span>&raquo;</a></p>
625
+ </div>
626
+
627
+ <!-- END ID="CONTENT" -->
628
+ </div>
629
+
630
+ <!-- END ID="MAIN_CONTENT" -->
631
+ <div id="main_sidebar">
632
+ <div id="globalcontent">
633
+ </div>
634
+
635
+ <!-- END ID="GLOBALCONTENT" -->
636
+ <div class="adwrapper">
637
+ <div class="sponsor300">
638
+
639
+ <!-- START UNDERWRITING -->
640
+ <!-- DFP AD MANAGER -->
641
+ <script type="text/javascript" language="javascript">
642
+ <!--
643
+ try{
644
+ DFP.renderLocation(0);
645
+ }catch(e){
646
+ NPR.messaging.warning('Problem fetching/rendering ad server code.','DFP.renderlocation',NPR.messaging.constants.SPONSORSHIP_ERROR);
647
+ }
648
+ // -->
649
+ </script>
650
+ <!-- END UNDERWRITING -->
651
+
652
+ <!-- ADD LANGUAGE -->
653
+ <p class="left">NPR thanks our sponsors</p> <p class="right"><a href="http://www.npr.org/about/place/corpsupport/">Become an NPR Sponsor </a></p>
654
+
655
+ <!-- END LANGUAGE -->
656
+ </div>
657
+
658
+ <!-- END CLASS="SPONSOR300" -->
659
+ </div>
660
+
661
+ <!-- END CLASS="ADWRAPPER" -->
662
+ <div id="pluckdiscoverybucket">
663
+ <div id="mostpopular" class="bucketwrap viewviewed">
664
+ <div class="top">
665
+ <h3>most <span>popular</span></h3>
666
+ </div>
667
+
668
+ <!-- END CLASS="TOP" -->
669
+ <div class="bucket viewviewed">
670
+ <ul class="nav threetab">
671
+ <li class="selected"><a onclick="setClass('mostpopular','bucketwrap viewviewed');" class="tabviewed" href="javascript:void(0);">Viewed</a></li>
672
+ <li><a onclick="setClass('mostpopular','bucketwrap viewrec');" class="tabrec" href="javascript:void(0);">Recommended</a></li>
673
+ <li><a onclick="setClass('mostpopular','bucketwrap viewcomm');" class="tabcomm" href="javascript:void(0);">Commented</a></li>
674
+ </ul>
675
+ <ol class="line">
676
+ <li><a href="/templates/story/story.php?storyId=136028608&amp;ps=cprs">Uncovering A Grandfather's Secret Nazi Past</a></li>
677
+ <li><a href="/templates/story/story.php?storyId=136053462&amp;ps=cprs">Is The End Nigh? We'll Know Soon Enough</a></li>
678
+ <li><a href="/templates/story/story.php?storyId=136056357&amp;ps=cprs">The Lonely Island: The Hottest Thing In Fake Hip-Hop</a></li>
679
+ <li><a href="/templates/story/story.php?storyId=136056645&amp;ps=cprs">India On Bin Laden-Pakistan Link: 'We Told You So'</a></li>
680
+ </ol>
681
+ </div>
682
+
683
+ <!-- END CLASS="BUCKET VIEWVIEWED" -->
684
+ <div id="mostpopularpluck">
685
+ </div>
686
+
687
+ <!-- END ID="MOSTPOPULARPLUCK" -->
688
+ </div>
689
+
690
+ <!-- END ID="MOSTPOPULAR" CLASS="BUCKETWRAP VIEWVIEWED" -->
691
+ </div>
692
+
693
+ <!-- END ID="PLUCKDISCOVERYBUCKET" -->
694
+ <div id="googledonatead">
695
+ <script type="text/javascript" src="http://partner.googleadservices.com/gampad/google_service.js">
696
+ </script>
697
+ <script type="text/javascript">
698
+ AddNamespace("NPR.google");
699
+ </script>
700
+ <script type="text/javascript">
701
+ <!--
702
+ if(NPR.google.adsEnabled == null || NPR.google.adsEnabled == false)
703
+ {
704
+ try {
705
+ GS_googleAddAdSenseService("ca-pub-6667715786680641");
706
+ GS_googleEnableAllServices();
707
+ }catch(e){
708
+ }
709
+ }
710
+ // -->
711
+ </script>
712
+
713
+ <script type="text/javascript">
714
+ <!--
715
+ if(NPR.google.adsEnabled == null || NPR.google.adsEnabled == false)
716
+ {
717
+ try {
718
+ GA_googleAddSlot("ca-pub-6667715786680641", "npr_ros_300x138_a");
719
+ GA_googleAddSlot("ca-pub-6667715786680641", "npr_ros_300x138_b");
720
+ GA_googleAddSlot("ca-pub-6667715786680641", "npr_ros_300x138_c");
721
+ }catch(e){
722
+ }
723
+ }
724
+ // -->
725
+ </script>
726
+
727
+ <script type="text/javascript">
728
+ <!--
729
+ if(NPR.google.adsEnabled == null || NPR.google.adsEnabled == false)
730
+ {
731
+ try {
732
+ GA_googleFetchAds();
733
+ }catch(e){
734
+ }
735
+ }
736
+ // -->
737
+ </script>
738
+
739
+ <script type="text/javascript">
740
+ <!--
741
+ try {
742
+ GA_googleFillSlot("npr_ros_300x138_c");
743
+ }catch(e){
744
+ }
745
+ // -->
746
+ </script>
747
+
748
+ <script type="text/javascript">
749
+ NPR.google.adsEnabled = true;
750
+ </script>
751
+ </div>
752
+
753
+ <!-- END ID="GOOGLEDONATEAD" -->
754
+ <div id="globalcontentbottom">
755
+ <script type="text/javascript" src="http://partner.googleadservices.com/gampad/google_service.js">
756
  <!--
1
757
  if(NPR.google.adsEnabled == null || NPR.google.adsEnabled == false)
2
758
  {
3
759
  }
4
760
  // -->
5
761
  <!--
6
762
  if(NPR.google.adsEnabled == null || NPR.google.adsEnabled == false)
7
763
  {
8
764
  }
9
765
  // -->
10
766
  <!--
11
767
  if(NPR.google.adsEnabled == null || NPR.google.adsEnabled == false)
12
768
  {
13
769
  }
14
770
  // -->
15
771
  <!--
16
772
  try {
17
773
  GA_googleFillSlot("npr_ros_300x138_a");
18
774
  }catch(e){
19
775
  }
20
776
  // -->
21
777
  <!--
22
778
  try {
23
779
  GA_googleFillSlot("npr_ros_300x138_b");
24
780
  }catch(e){
25
781
  }
26
782
  // -->
783
+ </div>
784
+
785
+ <!-- END ID="GLOBALCONTENTBOTTOM" -->
786
+ </div>
787
+
788
+ <!-- END ID="MAIN_SIDEBAR" -->
789
+ <div class="spacer">
790
+ &nbsp;
791
+ </div>
792
+ </div>
793
+
794
+ <!-- END ID="WRAPPER_MAIN" -->
795
+ </div>
796
+
797
+ <!-- END ID="WRAPPER" -->
798
+ <div id="footer">
799
+
800
+ <div class="footerwrap">
801
+
802
+ <div class="everywhere">
803
+ <h3><a href="/services/">npr <span>always on</span></a></h3>
804
+ <ul>
805
+ <li><a href="/email/" class="newsletter">Newsletters</a></li>
806
+ <li><a href="/podcasts/" class="pod">Podcasts</a></li>
807
+ <li><a href="/services/mobile/" class="mobile">Mobile</a></li>
808
+ <li><a href="/rss/" class="rss">RSS Feeds</a></li>
809
+ <li><a href="/services/desktop.html" class="widget">Widgets</a></li>
810
+ <li><a href="/api/" class="api">API</a></li>
811
+ <li><a href="/services/radio.html" class="radio">Radio</a></li>
812
+ </ul>
813
+ </div>
814
+
815
+ <div class="listbg">
816
+
817
+ <div class="col first">
818
+ <h4><a href="/sections/news/">news</a></h4>
819
+ <ul>
820
+ <li><a href="/sections/us/">U.S.</a></li>
821
+ <li><a href="/sections/world/">World</a></li>
822
+ <li><a href="/sections/opinion/">Opinion</a></li>
823
+ <li><a href="/sections/politics">Politics</a></li>
824
+ <li><a href="/sections/business/">Business</a></li>
825
+ <li><a href="/sections/technology/">Technology</a></li>
826
+ <li><a href="/sections/science/">Science</a></li>
827
+ <li><a href="/sections/health/">Health</a></li>
828
+ <li><a href="/sections/sports/">Sports</a></li>
829
+ </ul>
830
+ </div>
831
+
832
+ <div class="col">
833
+ <h4><a href="/sections/arts/">arts &amp; life</a></h4>
834
+ <ul>
835
+ <li><a href="/sections/books/">Books</a></li>
836
+ <li><a href="/sections/movies/">Movies</a></li>
837
+ <li><a href="/sections/pop-culture/">Pop Culture</a></li>
838
+ <li><a href="/sections/food/">Food</a></li>
839
+ <li><a href="/sections/performing-arts/">Performing Arts</a></li>
840
+ <li><a href="/sections/games-humor/">Games &amp; Humor</a></li>
841
+ </ul>
842
+ </div>
843
+
844
+ <div class="col">
845
+ <h4><a href="/music/">music</a></h4>
846
+ <ul>
847
+ <li><a href="/sections/concerts/">Concerts</a></li>
848
+ <li><a href="/sections/music-lists/">Music Lists</a></li>
849
+ <li><a href="/sections/music-interviews/">Music Interviews</a></li>
850
+ <li><a href="/sections/music-news/">Music News</a></li>
851
+ <li><a href="/music/blogs/">Music Blogs</a></li>
852
+ <li><a href="/music/genres/rock-pop-folk/">Rock/Pop/Folk</a></li>
853
+ <li><a href="/music/genres/jazz-blues/">Jazz &amp; Blues</a></li>
854
+ <li><a href="/music/genres/classical/">Classical</a></li>
855
+ <li><a href="/templates/artist/artist_index.php?filter=A">Browse Artists A-Z</a></li>
856
+ </ul>
857
+
858
+ <ul>
859
+ <li><a href="/programs/all-songs-considered/">All Songs Considered</a></li>
860
+ <li><a href="http://www.fromthetop.org/">From The Top</a></li>
861
+ <li><a href="/programs/jazzset/">JazzSet</a></li>
862
+ <li><a href="/programs/piano-jazz/">Marian McPartland's <span>Piano Jazz</span></a></li>
863
+ <li><a href="/series/mountain-stage/">Mountain Stage</a></li>
864
+ <li><a href="/series/4703895/song-of-the-day">Song of the Day</a></li>
865
+ <li><a href="http://www.thistleradio.com/">The Thistle &amp; Shamrock</a></li>
866
+ <li><a href="/programs/world-cafe/">World Cafe</a></li>
867
+ <li><a href="/programs/world-of-opera/">World Of Opera</a></li>
868
+ </ul>
869
+ </div>
870
+
871
+ <div class="col">
872
+ <h4><a href="/programs/">programs a-z</a></h4>
873
+ <ul>
874
+ <li><a href="/programs/morning-edition/">Morning Edition</a></li>
875
+ <li><a href="/programs/all-things-considered/">All Things Considered</a></li>
876
+ <li><a href="/programs/fresh-air/">Fresh Air</a></li>
877
+ <li><a href="http://thedianerehmshow.org">The Diane Rehm Show</a></li>
878
+ <li><a href="http://www.onthemedia.org">On The Media</a></li>
879
+ <li><a href="http://www.onpointradio.org/">On Point</a></li>
880
+ <li><a href="/programs/talk-of-the-nation/">Talk of the Nation</a></li>
881
+ <li><a href="/programs/tell-me-more/">Tell Me More</a></li>
882
+ <li><a href="/programs/weekend-edition-saturday/">Weekend Edition <span>Saturday</span></a></li>
883
+ <li><a href="/programs/weekend-edition-sunday/">Weekend Edition Sunday</a></li>
884
+ <li><a href="http://www.cartalk.com">Car Talk</a></li>
885
+ <li><a href="/programs/wait-wait-dont-tell-me/">Wait Wait...Don't Tell Me!</a></li>
886
+ </ul>
887
+ </div>
888
+
889
+ <div class="col">
890
+ <h4>listen</h4>
891
+ <ul>
892
+ <li><a href="javascript:getNewsCast();">Hourly News</a></li>
893
+ <li><a href="javascript:getProgramStream();">NPR Program Stream</a></li>
894
+ <li><a href="/audiohelp/progstream.html"><span>Schedule</span></a></li>
895
+ <li><a href="/stations/">Find Station Streams</a></li>
896
+ </ul>
897
+ </div>
898
+
899
+ <div class="col last">
900
+ <h4>more</h4>
901
+ <ul>
902
+ <li><a href="/sections/multimedia/">Multimedia</a></li>
903
+ <li><a href="/sections/columns/">Columns</a></li>
904
+ <li><a href="/blogs/">Blogs</a></li>
905
+ <li><a href="/sections/analysis/">Analysis</a></li>
906
+ <li><a href="/sections/commentary/">Commentary</a></li>
907
+ <li><a href="/ombudsman/">NPR Ombudsman</a></li>
908
+ <li><a href="http://topics.npr.org/">Topic Index</a></li>
909
+ </ul>
910
+ <h4><a href="/about/">about</a></h4>
911
+ <ul>
912
+ <li><a href="/about/">This Is NPR</a></li>
913
+ <li><a href="/about/support/">Support and Sponsor</a></li>
914
+ <li><a href="/about/press/">Press Room</a></li>
915
+ <li><a href="/about/careers/">Careers</a></li>
916
+ <li><a href="http://shop.npr.org">NPR Shop</a></li>
917
+ <li><a href="/contact/">Contact Us</a></li>
918
+ </ul>
919
+ </div>
920
+
921
+ </div>
922
+
923
+ </div>
924
+
925
+ <ul class="bottomlist">
926
+ <li>Copyright <script type="text/javascript">
927
+ year = new Date();
928
+ document.write(year.getFullYear());
929
+ </script> NPR</li>
930
+ <li><a href="/about/termsofuse.html">Terms of Use</a></li>
931
+ <li><a href="/about/privacypolicy.html">Privacy Policy</a></li>
932
+ <li><a href="/about/permissioninfo.html">Permissions</a></li>
933
+ <li><a href="/corrections/">Corrections</a></li>
934
+ <li><a href="http://thin.npr.org">Text-Only Site</a></li>
935
+ <li class="help"><a href="/help/">Help</a></li>
936
+ <li class="contact"><a href="/contact/">Contact Us</a></li>
937
+ <li class="donate"><a href="/stations/donate/?ps=stbn">Donate</a></li>
938
+ <li class="pbs">Our partner in public broadcasting <a href="http://www.pbs.org"><img src="http://media.npr.org/chrome/news/pbs_logo.gif" alt="pbs" /></a></li>
939
+ </ul>
940
+
941
+ </div>
942
+ <script type="text/javascript" src="/include/javascript/test/test1.js"></script>
943
+ </div>
944
+
945
+ <!-- END ID="SECTIONWRAP" CLASS="ARTSPAGE" -->
946
+ <script type="text/javascript">
947
+ var pageTrackingCPTV = function() {
948
+ try {
949
+ AddNamespace("NPR.metrics");
950
+ NPR.metrics.vstag = {"title":"136021711-Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby","omnitureContentType":"1","storyId":"136021711","hierarchy":"Arts+%26+Life,Food,Recipes","channel":"1139","date":"20110506","pageTypeId":"1","orgId":"1","segNum":"15","programId":"3","storyLength":1748,"byline":"NPR+Staff","primaryTopic":"1139","assignedTopics":"1139,1091,1053,1002"};
951
+ NPR.metrics.pageImpression();
952
+ } catch (e) {
953
+ NPR.messaging.exception(e, 'commonPageTrackView', 'document.ready', NPR.messaging.constants.METRICS_ERROR);
954
+ }
955
+ };
956
+ if($('docreadybind').length) {
957
+ $('docreadybind').bind('click', pageTrackingCPTV);
958
+ } else {
959
+ $(document).ready(pageTrackingCPTV);
960
+ }
961
+ </script>
962
+ <noscript>
963
+ <img alt="" src="/images/zag.gif?Log=1&amp;v_jd=1&amp;dt=136021711-Bourbon+Balls+Give+A+Sweet+Kick+To+Kentucky+Derby&amp;omnitureContentType=1&amp;storyId=136021711&amp;hierarchy=Arts+%26+Life,Food,Recipes&amp;channel=1139&amp;date=20110506&amp;pageTypeId=1&amp;orgId=1&amp;segNum=15&amp;programId=3&amp;storyLength=1748&amp;byline=NPR+Staff&amp;primaryTopic=1139&amp;assignedTopics=1139,1091,1053,1002" border="0" width="1" height="1"/>
964
+ </noscript> <script type="text/javascript">
965
+ var _gaq = _gaq || [];
966
+ _gaq.push(['_setAccount', 'UA-5828686-4']);
967
+ _gaq.push(['_setCustomVar',1,'Breadcrumb','Arts & Life > Food > Recipes',3]);
968
+ _gaq.push(['_setCustomVar',2,'story ID','136021711',3]);
969
+ _gaq.push(['_setCustomVar',5,'programID','3',1]);
970
+
971
+ _gaq.push(['_trackPageview']);
972
+
973
+ (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
974
+
975
+ </script> </body></html>
976
+
977
+ <!-- Burned on demand at 2011-05-08 22:35:48-->