feedjira 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +31 -12
- data/CHANGELOG.md +15 -1
- data/Dangerfile +1 -0
- data/Gemfile +2 -1
- data/Rakefile +6 -1
- data/feedjira.gemspec +16 -14
- data/fixtures/vcr_cassettes/fetch_failure.yml +62 -0
- data/fixtures/vcr_cassettes/parse_error.yml +222 -0
- data/fixtures/vcr_cassettes/success.yml +281 -0
- data/lib/feedjira.rb +9 -0
- data/lib/feedjira/core_ext.rb +3 -3
- data/lib/feedjira/core_ext/date.rb +2 -1
- data/lib/feedjira/core_ext/string.rb +1 -1
- data/lib/feedjira/core_ext/time.rb +19 -16
- data/lib/feedjira/date_time_utilities.rb +24 -0
- data/lib/feedjira/date_time_utilities/date_time_language_parser.rb +22 -0
- data/lib/feedjira/date_time_utilities/date_time_pattern_parser.rb +29 -0
- data/lib/feedjira/feed.rb +27 -18
- data/lib/feedjira/feed_entry_utilities.rb +15 -17
- data/lib/feedjira/feed_utilities.rb +26 -21
- data/lib/feedjira/parser/atom.rb +9 -8
- data/lib/feedjira/parser/atom_entry.rb +10 -13
- data/lib/feedjira/parser/atom_feed_burner.rb +8 -10
- data/lib/feedjira/parser/atom_feed_burner_entry.rb +11 -14
- data/lib/feedjira/parser/atom_youtube.rb +20 -0
- data/lib/feedjira/parser/atom_youtube_entry.rb +29 -0
- data/lib/feedjira/parser/google_docs_atom.rb +6 -6
- data/lib/feedjira/parser/google_docs_atom_entry.rb +11 -11
- data/lib/feedjira/parser/itunes_rss.rb +39 -22
- data/lib/feedjira/parser/itunes_rss_category.rb +38 -0
- data/lib/feedjira/parser/itunes_rss_item.rb +28 -20
- data/lib/feedjira/parser/itunes_rss_owner.rb +3 -4
- data/lib/feedjira/parser/podlove_chapter.rb +20 -0
- data/lib/feedjira/parser/rss.rb +10 -8
- data/lib/feedjira/parser/rss_entry.rb +17 -21
- data/lib/feedjira/parser/rss_feed_burner.rb +4 -6
- data/lib/feedjira/parser/rss_feed_burner_entry.rb +23 -28
- data/lib/feedjira/parser/rss_image.rb +15 -0
- data/lib/feedjira/preprocessor.rb +2 -2
- data/lib/feedjira/version.rb +1 -1
- data/spec/feedjira/date_time_utilities_spec.rb +41 -0
- data/spec/feedjira/feed_entry_utilities_spec.rb +23 -19
- data/spec/feedjira/feed_spec.rb +109 -74
- data/spec/feedjira/feed_utilities_spec.rb +65 -63
- data/spec/feedjira/parser/atom_entry_spec.rb +54 -34
- data/spec/feedjira/parser/atom_feed_burner_entry_spec.rb +27 -20
- data/spec/feedjira/parser/atom_feed_burner_spec.rb +32 -30
- data/spec/feedjira/parser/atom_spec.rb +50 -48
- data/spec/feedjira/parser/atom_youtube_entry_spec.rb +86 -0
- data/spec/feedjira/parser/atom_youtube_spec.rb +43 -0
- data/spec/feedjira/parser/google_docs_atom_entry_spec.rb +5 -4
- data/spec/feedjira/parser/google_docs_atom_spec.rb +6 -6
- data/spec/feedjira/parser/itunes_rss_item_spec.rb +33 -29
- data/spec/feedjira/parser/itunes_rss_owner_spec.rb +10 -9
- data/spec/feedjira/parser/itunes_rss_spec.rb +83 -30
- data/spec/feedjira/parser/podlove_chapter_spec.rb +37 -0
- data/spec/feedjira/parser/rss_entry_spec.rb +50 -33
- data/spec/feedjira/parser/rss_feed_burner_entry_spec.rb +55 -33
- data/spec/feedjira/parser/rss_feed_burner_spec.rb +31 -26
- data/spec/feedjira/parser/rss_spec.rb +56 -24
- data/spec/feedjira/preprocessor_spec.rb +11 -3
- data/spec/sample_feeds.rb +29 -21
- data/spec/sample_feeds/AmazonWebServicesBlog.xml +797 -797
- data/spec/sample_feeds/AtomEscapedHTMLInPreTag.xml +13 -0
- data/spec/sample_feeds/CRE.xml +5849 -0
- data/spec/sample_feeds/FeedBurnerXHTML.xml +400 -400
- data/spec/sample_feeds/ITunesWithSingleQuotedAttributes.xml +67 -0
- data/spec/sample_feeds/PaulDixExplainsNothing.xml +175 -175
- data/spec/sample_feeds/PaulDixExplainsNothingAlternate.xml +175 -175
- data/spec/sample_feeds/PaulDixExplainsNothingFirstEntryContent.xml +16 -16
- data/spec/sample_feeds/PaulDixExplainsNothingWFW.xml +174 -174
- data/spec/sample_feeds/TenderLovemaking.xml +12 -2
- data/spec/sample_feeds/TrotterCashionHome.xml +611 -611
- data/spec/sample_feeds/TypePadNews.xml +368 -368
- data/spec/sample_feeds/itunes.xml +18 -2
- data/spec/sample_feeds/pet_atom.xml +229 -229
- data/spec/sample_feeds/youtube_atom.xml +395 -0
- data/spec/spec_helper.rb +6 -0
- metadata +112 -27
@@ -1,19 +1,19 @@
|
|
1
|
-
<p>Last week I released the first version of a <a href="http://www.pauldix.net/2009/01/sax-machine-sax-parsing-made-easy.html">SAX based XML parsing library called SAX-Machine</a>. It uses Nokogiri, which uses libxml, so it's pretty fast. However, I felt that it could be even faster. The only question was how to make a ruby library that is already using c underneath perform better. Since I've never written a Ruby C extension and it's been a few years since I've touched C, I decided it would be a good educational experience to give it a try.</p>
|
2
|
-
|
3
|
-
<p>First, let's look into how Nokogiri and SAX-Machine perform a parse. The syntax for SAX-Machine builds up a set of class variables (actually, instance variables on a class object) that describe what you're interested in parsing. So when you see something like this:
|
4
|
-
</p><script src="http://gist.github.com/50549.js"></script><p>
|
5
|
-
It calls the 'element' and 'elements' methods inserted by the SAXMachine module that build up ruby objects that describe what XML tags we're interested in for the Entry class. That's all pretty straight forward and not really the source of any slowdown in the parsing process. These calls only happen once, when you first load the class.
|
6
|
-
|
7
|
-
</p><p>Things get interesting when you run a parse. So you run Entry.parse(some_xml). That makes the call to Nokogiri, which in turn makes a call to libxml. Libxml then parses over the stream (or string) and makes calls to C methods (in Nokogiri) on certain events. For our purposes, the most interesting are start_element, end_element, and characters_func. The C code in Nokogiri for these is basic. It simply converts those C variables into Ruby ones and then makes calls to whatever instance of Nokogiri::XML:SAX::Document (a Ruby object) is associated with this parse. This is where SAXMachine comes back in. It has handlers for these events that match up the tags with the previously defined SAXMachine objects attached to the Entry class. It ignores the events that don't match a tag (however, it still needs to determine if the tag should be ignored).</p>
|
8
|
-
|
9
|
-
<p>The only possible place I saw to speed things up was to push more of SAX event handling down into the C code. Unfortunately, the only way to do this was to abandon Nokogiri and write my own code to interface with libxml. I used the xml_sax_parser.c from Nokogiri as a base and added to it. I changed it so the SAXMachine definitions of what was interesting would be stored in C. I then changed the SAX handling code to capture the events in C and determine if a tag was of interest there before sending it off to the Ruby objects. The end result is that calls are only made to Ruby when there is an actual event of interest. Thus, I avoid doing any comparisons in Ruby and those classes are simply wrappers that call out to the correct value setters.</p>
|
10
|
-
|
11
|
-
<p>Here are the results of a quick speed comparison against the Nokogiri SAXMachine, parsing my atom feed using <a href="http://gist.github.com/47938">code from my last post</a>.</p>
|
12
|
-
<pre> user system total real<br>sax c 0.060000 0.000000 0.060000 ( 0.069990)<br>sax nokogiri 0.500000 0.010000 0.510000 ( 0.520278)<br></pre><p>
|
13
|
-
The SAX C is 7.4 times faster than SAX Nokogiri. Now, that doesn't seem like a whole lot, but I think it's quite good considering it was against a library that was already half in C. It's even more punctuated when you look at the comparison of these two against rfeedparser.
|
14
|
-
</p><pre> user system total real<br>sax c 0.060000 0.000000 0.060000 ( 0.069990)<br>sax nokogiri 0.500000 0.010000 0.510000 ( 0.520278)<br>rfeedparser 13.770000 1.730000 15.500000 ( 15.690309)<br></pre>
|
15
|
-
<p>The SAX C version is 224 times faster than rfeedparser! The 7 times multiple from the Nokogiri version of SAXMachine really makes a difference. Unfortunately, I really only wrote this code as a test. It's not even close to something I would use for real. It has memory leaks, isn't thread safe, is completely unreadable, and has hidden bugs that I know about. You can take a look at it in all its misery on the <a href="http://github.com/pauldix/sax-machine/tree/c-refactor">c-rafactor branch of SAXMachine on github</a>. Even though the code is awful, I think it's interesting that there can be this much variability in performance on Ruby libraries that are using C.</p>
|
16
|
-
|
1
|
+
<p>Last week I released the first version of a <a href="http://www.pauldix.net/2009/01/sax-machine-sax-parsing-made-easy.html">SAX based XML parsing library called SAX-Machine</a>. It uses Nokogiri, which uses libxml, so it's pretty fast. However, I felt that it could be even faster. The only question was how to make a ruby library that is already using c underneath perform better. Since I've never written a Ruby C extension and it's been a few years since I've touched C, I decided it would be a good educational experience to give it a try.</p>
|
2
|
+
|
3
|
+
<p>First, let's look into how Nokogiri and SAX-Machine perform a parse. The syntax for SAX-Machine builds up a set of class variables (actually, instance variables on a class object) that describe what you're interested in parsing. So when you see something like this:
|
4
|
+
</p><script src="http://gist.github.com/50549.js"></script><p>
|
5
|
+
It calls the 'element' and 'elements' methods inserted by the SAXMachine module that build up ruby objects that describe what XML tags we're interested in for the Entry class. That's all pretty straight forward and not really the source of any slowdown in the parsing process. These calls only happen once, when you first load the class.
|
6
|
+
|
7
|
+
</p><p>Things get interesting when you run a parse. So you run Entry.parse(some_xml). That makes the call to Nokogiri, which in turn makes a call to libxml. Libxml then parses over the stream (or string) and makes calls to C methods (in Nokogiri) on certain events. For our purposes, the most interesting are start_element, end_element, and characters_func. The C code in Nokogiri for these is basic. It simply converts those C variables into Ruby ones and then makes calls to whatever instance of Nokogiri::XML:SAX::Document (a Ruby object) is associated with this parse. This is where SAXMachine comes back in. It has handlers for these events that match up the tags with the previously defined SAXMachine objects attached to the Entry class. It ignores the events that don't match a tag (however, it still needs to determine if the tag should be ignored).</p>
|
8
|
+
|
9
|
+
<p>The only possible place I saw to speed things up was to push more of SAX event handling down into the C code. Unfortunately, the only way to do this was to abandon Nokogiri and write my own code to interface with libxml. I used the xml_sax_parser.c from Nokogiri as a base and added to it. I changed it so the SAXMachine definitions of what was interesting would be stored in C. I then changed the SAX handling code to capture the events in C and determine if a tag was of interest there before sending it off to the Ruby objects. The end result is that calls are only made to Ruby when there is an actual event of interest. Thus, I avoid doing any comparisons in Ruby and those classes are simply wrappers that call out to the correct value setters.</p>
|
10
|
+
|
11
|
+
<p>Here are the results of a quick speed comparison against the Nokogiri SAXMachine, parsing my atom feed using <a href="http://gist.github.com/47938">code from my last post</a>.</p>
|
12
|
+
<pre> user system total real<br>sax c 0.060000 0.000000 0.060000 ( 0.069990)<br>sax nokogiri 0.500000 0.010000 0.510000 ( 0.520278)<br></pre><p>
|
13
|
+
The SAX C is 7.4 times faster than SAX Nokogiri. Now, that doesn't seem like a whole lot, but I think it's quite good considering it was against a library that was already half in C. It's even more punctuated when you look at the comparison of these two against rfeedparser.
|
14
|
+
</p><pre> user system total real<br>sax c 0.060000 0.000000 0.060000 ( 0.069990)<br>sax nokogiri 0.500000 0.010000 0.510000 ( 0.520278)<br>rfeedparser 13.770000 1.730000 15.500000 ( 15.690309)<br></pre>
|
15
|
+
<p>The SAX C version is 224 times faster than rfeedparser! The 7 times multiple from the Nokogiri version of SAXMachine really makes a difference. Unfortunately, I really only wrote this code as a test. It's not even close to something I would use for real. It has memory leaks, isn't thread safe, is completely unreadable, and has hidden bugs that I know about. You can take a look at it in all its misery on the <a href="http://github.com/pauldix/sax-machine/tree/c-refactor">c-rafactor branch of SAXMachine on github</a>. Even though the code is awful, I think it's interesting that there can be this much variability in performance on Ruby libraries that are using C.</p>
|
16
|
+
|
17
17
|
<p>I could actually turn this into a legitimate working version, but it would take more work than I think it's worth at this point. Also, I'm not excited about the idea of dealing with C issues in SAXMachine. I would be more excited for it if I could get this type of SAX parsing thing into Nokogiri (in addition to the one that is there now). For now, I'll move on to using the Nokogiri version of SAXMachine to create a feed parsing library.</p><div class="feedflare">
|
18
18
|
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=9Q8qfQ.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=9Q8qfQ.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=rLK96Z.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=rLK96Z.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=B95sFg.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=B95sFg.p" border="0"></img></a>
|
19
19
|
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/519925023" height="1" width="1"/>
|
@@ -1,174 +1,174 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
|
3
|
-
<title>Paul Dix Explains Nothing</title>
|
4
|
-
|
5
|
-
<link rel="alternate" type="text/html" href="http://www.pauldix.net/" />
|
6
|
-
<id>tag:typepad.com,2003:weblog-108605</id>
|
7
|
-
<updated>2009-01-22T10:50:22-05:00</updated>
|
8
|
-
<subtitle>Entrepreneurship, programming, software development, politics, NYC, and random thoughts.</subtitle>
|
9
|
-
<generator uri="http://www.typepad.com/">TypePad</generator>
|
10
|
-
<link rel="self" href="http://feeds.feedburner.com/PaulDixExplainsNothing" type="application/atom+xml" /><entry>
|
11
|
-
<title>Making a Ruby C library even faster</title>
|
12
|
-
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/519925023/making-a-ruby-c-library-even-faster.html" />
|
13
|
-
<link rel="replies" type="text/html" href="http://www.pauldix.net/2009/01/making-a-ruby-c-library-even-faster.html" thr:count="1" thr:updated="2009-01-22T16:30:53-05:00" />
|
14
|
-
<id>tag:typepad.com,2003:post-61753462</id>
|
15
|
-
<published>2009-01-22T10:50:22-05:00</published>
|
16
|
-
<updated>2009-01-22T16:30:53-05:00</updated>
|
17
|
-
<summary>Last week I released the first version of a SAX based XML parsing library called SAX-Machine. It uses Nokogiri, which uses libxml, so it's pretty fast. However, I felt that it could be even faster. The only question was how...</summary>
|
18
|
-
<author>
|
19
|
-
<name>Paul Dix</name>
|
20
|
-
</author>
|
21
|
-
<category scheme="http://www.sixapart.com/ns/types#category" term="Ruby" />
|
22
|
-
<category scheme="http://www.sixapart.com/ns/types#category" term="Another Category" />
|
23
|
-
|
24
|
-
|
25
|
-
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/"><p>Last week I released the first version of a <a href="http://www.pauldix.net/2009/01/sax-machine-sax-parsing-made-easy.html">SAX based XML parsing library called SAX-Machine</a>. It uses Nokogiri, which uses libxml, so it's pretty fast. However, I felt that it could be even faster. The only question was how to make a ruby library that is already using c underneath perform better. Since I've never written a Ruby C extension and it's been a few years since I've touched C, I decided it would be a good educational experience to give it a try.</p>
|
26
|
-
|
27
|
-
<p>First, let's look into how Nokogiri and SAX-Machine perform a parse. The syntax for SAX-Machine builds up a set of class variables (actually, instance variables on a class object) that describe what you're interested in parsing. So when you see something like this
|
28
|
-
</p><script src="http://gist.github.com/50549.js"></script><p>
|
29
|
-
It calls the 'element' and 'elements' methods inserted by the SAXMachine module that build up ruby objects that describe what XML tags we're interested in for the Entry class. That's all pretty straight forward and not really the source of any slowdown in the parsing process. These calls only happen once, when you first load the class
|
30
|
-
|
31
|
-
</p><p>Things get interesting when you run a parse. So you run Entry.parse(some_xml). That makes the call to Nokogiri, which in turn makes a call to libxml. Libxml then parses over the stream (or string) and makes calls to C methods (in Nokogiri) on certain events. For our purposes, the most interesting are start_element, end_element, and characters_func. The C code in Nokogiri for these is basic. It simply converts those C variables into Ruby ones and then makes calls to whatever instance of Nokogiri::XML:SAX::Document (a Ruby object) is associated with this parse. This is where SAXMachine comes back in. It has handlers for these events that match up the tags with the previously defined SAXMachine objects attached to the Entry class. It ignores the events that don't match a tag (however, it still needs to determine if the tag should be ignored).</p>
|
32
|
-
|
33
|
-
<p>The only possible place I saw to speed things up was to push more of SAX event handling down into the C code. Unfortunately, the only way to do this was to abandon Nokogiri and write my own code to interface with libxml. I used the xml_sax_parser.c from Nokogiri as a base and added to it. I changed it so the SAXMachine definitions of what was interesting would be stored in C. I then changed the SAX handling code to capture the events in C and determine if a tag was of interest there before sending it off to the Ruby objects. The end result is that calls are only made to Ruby when there is an actual event of interest. Thus, I avoid doing any comparisons in Ruby and those classes are simply wrappers that call out to the correct value setters.</p>
|
34
|
-
|
35
|
-
<p>Here are the results of a quick speed comparison against the Nokogiri SAXMachine, parsing my atom feed using <a href="http://gist.github.com/47938">code from my last post</a>.</p>
|
36
|
-
<pre> user system total real<br>sax c 0.060000 0.000000 0.060000 ( 0.069990)<br>sax nokogiri 0.500000 0.010000 0.510000 ( 0.520278)<br></pre><p>
|
37
|
-
The SAX C is 7.4 times faster than SAX Nokogiri. Now, that doesn't seem like a whole lot, but I think it's quite good considering it was against a library that was already half in C. It's even more punctuated when you look at the comparison of these two against rfeedparser
|
38
|
-
</p><pre> user system total real<br>sax c 0.060000 0.000000 0.060000 ( 0.069990)<br>sax nokogiri 0.500000 0.010000 0.510000 ( 0.520278)<br>rfeedparser 13.770000 1.730000 15.500000 ( 15.690309)<br></pre>
|
39
|
-
<p>The SAX C version is 224 times faster than rfeedparser! The 7 times multiple from the Nokogiri version of SAXMachine really makes a difference. Unfortunately, I really only wrote this code as a test. It's not even close to something I would use for real. It has memory leaks, isn't thread safe, is completely unreadable, and has hidden bugs that I know about. You can take a look at it in all its misery on the <a href="http://github.com/pauldix/sax-machine/tree/c-refactor">c-rafactor branch of SAXMachine on github</a>. Even though the code is awful, I think it's interesting that there can be this much variability in performance on Ruby libraries that are using C.</p>
|
40
|
-
|
41
|
-
<p>I could actually turn this into a legitimate working version, but it would take more work than I think it's worth at this point. Also, I'm not excited about the idea of dealing with C issues in SAXMachine. I would be more excited for it if I could get this type of SAX parsing thing into Nokogiri (in addition to the one that is there now). For now, I'll move on to using the Nokogiri version of SAXMachine to create a feed parsing library.</p><div class="feedflare">
|
42
|
-
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=9Q8qfQ.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=9Q8qfQ.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=rLK96Z.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=rLK96Z.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=B95sFg.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=B95sFg.p" border="0"></img></a>
|
43
|
-
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/519925023" height="1" width="1"/></content>
|
44
|
-
|
45
|
-
<wfw:commentRss>this is the new val</wfw:commentRss>
|
46
|
-
<feedburner:origLink>http://www.pauldix.net/2009/01/making-a-ruby-c-library-even-faster.html</feedburner:origLink></entry>
|
47
|
-
<entry>
|
48
|
-
<title>SAX Machine - SAX Parsing made easy</title>
|
49
|
-
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/514044516/sax-machine-sax-parsing-made-easy.html" />
|
50
|
-
<link rel="replies" type="text/html" href="http://www.pauldix.net/2009/01/sax-machine-sax-parsing-made-easy.html" thr:count="4" thr:updated="2009-01-21T23:16:44-05:00" />
|
51
|
-
<id>tag:typepad.com,2003:post-61473064</id>
|
52
|
-
<published>2009-01-16T09:47:32-05:00</published>
|
53
|
-
<updated>2009-01-21T23:16:44-05:00</updated>
|
54
|
-
<summary>For a while now I've been wanting to create a ruby library for feed (Atom, RSS) parsing. I know there are already some out there, but for one reason or another each of them falls short for my needs. The...</summary>
|
55
|
-
<author>
|
56
|
-
<name>Paul Dix</name>
|
57
|
-
</author>
|
58
|
-
<category scheme="http://www.sixapart.com/ns/types#category" term="Ruby" />
|
59
|
-
|
60
|
-
|
61
|
-
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/"><p>For a while now I've been wanting to create a ruby library for feed (Atom, RSS) parsing. I know there are already some out there, but for one reason or another each of them falls short for my needs. The first part of this effort requires me to decide how I want to go about parsing the feeds. One of the primary goals is speed. I want this thing to be fast. So I decided to look into SAX parsing. Further, I wanted to use Nokogiri because I'm going to be doing a few other things with the posts like pulling out links and tags, and sanitizing entries.</p><p>SAX parsing is an event based parsing model. It fires events during parsing for things like start_tag, end_tag, start_document, end_document, and characters. Nokogiri contains a very light weight wrapper around libxml's SAX parsing. Using this I started creating a basic declarative syntax for registering parsing events and mapping them into objects and values. The end result is <a href="http://github.com/pauldix/sax-machine">SAX Machine, a declarative SAX parser for parsing xml into ruby objects</a>.</p><p>Some of the syntax was inspired by <a href="http://github.com/jnunemaker/happymapper">John Nunemaker's Happy Mapper</a>. However, the behavior is a bit different. There are probably many more features I am going to add in as I use it to create my feed parsing library, but the basic stuff is there right now.</p><p>Here's an example that uses SAX Machine to parse a FeedBurner atom feed into ruby objects.</p><script src="http://gist.github.com/47938.js"></script><p>
|
62
|
-
|
63
|
-
So in a few short lines of code this parses feedburner feeds exactly how I want them. It's also pretty fast. Here's a benchmark against rfeedparser running against the atom feed from this blog 100 times
|
64
|
-
</p><pre>feedzirra 1.430000 0.020000 1.450000 ( 1.472081)<br>rfeedparser 15.780000 0.580000 16.360000 ( 16.768889)<br></pre><p>
|
65
|
-
It's about 11 times faster in this test. I have to do more tests before I feel comfortable with that figure, but it's a promising start. More importantly this will enable me to make a feed parser that's flexible, extensible, and readable.</p><p>SAX Machine is available as a gem as long as you have github added as a gem source. Just do gem install pauldix-sax-machine to get the party started (<strong>UPDATE:</strong> for some reason it's not built yet. looking into it. <strong>UPDATE 2: </strong>it's built and ready to go. thanks to nakajima). Also, special thanks to <a href="http://www.brynary.com/">Bryan Helmkamp</a> for helping me turn my initial spike into a decently tested and refactored version (even if it did slow my benchmark down by a factor of 4).</p><div class="feedflare">
|
66
|
-
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=2VN4Om.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=2VN4Om.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=5MHPvx.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=5MHPvx.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=nMpoB4.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=nMpoB4.p" border="0"></img></a>
|
67
|
-
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/514044516" height="1" width="1"/></content>
|
68
|
-
|
69
|
-
|
70
|
-
<feedburner:origLink>http://www.pauldix.net/2009/01/sax-machine-sax-parsing-made-easy.html</feedburner:origLink></entry>
|
71
|
-
<entry>
|
72
|
-
<title>Professional Goals for 2009</title>
|
73
|
-
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/499546917/professional-goals-for-2009.html" />
|
74
|
-
<link rel="replies" type="text/html" href="http://www.pauldix.net/2008/12/professional-goals-for-2009.html" thr:count="3" thr:updated="2009-01-01T10:17:04-05:00" />
|
75
|
-
<id>tag:typepad.com,2003:post-60634768</id>
|
76
|
-
<published>2008-12-31T10:55:02-05:00</published>
|
77
|
-
<updated>2009-01-01T10:17:05-05:00</updated>
|
78
|
-
<summary>In two weeks I'll be making the transition from full time student to full time worker. Instead of worrying about grades and papers I'll have to get real work done. For the last four years I've set a lot of...</summary>
|
79
|
-
<author>
|
80
|
-
<name>Paul Dix</name>
|
81
|
-
</author>
|
82
|
-
|
83
|
-
|
84
|
-
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/"><p>In two weeks I'll be making the transition from full time student to full time worker. Instead of worrying about grades and papers I'll have to get real work done. For the last four years I've set a lot of goals for myself, but I've mainly been focused on the single goal of finishing school. Now that I've accomplished that it's time to figure out what the next steps are. Being that it's the eve of a new year, I thought a good way to start would be to outline exactly what I'd like to accomplish professionally in 2009. So here are some goals in no particular order
|
85
|
-
</p><ul>
|
86
|
-
<li>Write 48 well thought out blog posts (once a week for 48 weeks of the year)</li>
|
87
|
-
<li>Write an open source library that at least 2 people/organizations use in production</li>
|
88
|
-
<li>Contribute to a popular open source library/project</li>
|
89
|
-
<li>Finish a working version of <a href="http://www.pauldix.net/tahiti/index.html">Filterly</a> and use it daily</li>
|
90
|
-
<li>Learn a new programming language</li>
|
91
|
-
<li>Finish reading <a href="http://www.amazon.com/Introduction-Machine-Learning-Adaptive-Computation/dp/0262012111/ref=sr_1_3?ie=UTF8&amp;s=books&amp;qid=1230736254&amp;sr=1-3">this book on machine learning</a></li>
|
92
|
-
<li>Read <a href="http://www.amazon.com/Learning-Kernels-Regularization-Optimization-Computation/dp/0262194759/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1230736307&amp;sr=1-1">Learning With Kernels by Schlkopf and Smola</a></li>
|
93
|
-
<li>Be a contributing author to a book or shortcut</li>
|
94
|
-
<li>Present at a conference</li>
|
95
|
-
<li>Present at a users group</li>
|
96
|
-
<li>Help create a site that gets decent traffic (kind of non-specific, I know)
|
97
|
-
</li>
|
98
|
-
<li>Attend two conferences</li>
|
99
|
-
<li>Attend at least 8 <a href="http://nycruby.org/wiki/">nyc.rb (NYC Ruby) meetings</a></li>
|
100
|
-
<li>Attend at least 6 <a href="http://www.meetup.com/ny-tech/">NY Tech Meetups</a></li>
|
101
|
-
<li>Attend at least 4 <a href="http://www.meetup.com/BlueVC/">Columbia Blue Venture Community events</a> (hard because it conflicts with nyc.rb)</li>
|
102
|
-
<li>Attend at least 4 <a href="http://nextny.org/">NextNY</a> events</li>
|
103
|
-
<li>Do all the <a href="http://codekata.pragprog.com/">Code Katas</a> at least once (any language)</li>
|
104
|
-
</ul>
|
105
|
-
<p>That's all I've come up with so far. I probably won't be able to get everything on the list, but if I get most of the way there I'll be happy. A year may be too long of a development cycle for professional goals. It would be much more agile if I broke these down into two week sprints, but this will do for now. It gives me specific areas to focus my efforts over the next 12 months. What goals do you have for 2009?</p><div class="feedflare">
|
106
|
-
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=IP49Wo.O"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=IP49Wo.O" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=4Azfxm.o"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=4Azfxm.o" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=iwU8iG.o"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=iwU8iG.o" border="0"></img></a>
|
107
|
-
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/499546917" height="1" width="1"/></content>
|
108
|
-
|
109
|
-
|
110
|
-
<feedburner:origLink>http://www.pauldix.net/2008/12/professional-goals-for-2009.html</feedburner:origLink></entry>
|
111
|
-
<entry>
|
112
|
-
<title>Marshal data too short error with ActiveRecord</title>
|
113
|
-
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/383536354/marshal-data-to.html" />
|
114
|
-
<link rel="replies" type="text/html" href="http://www.pauldix.net/2008/09/marshal-data-to.html" thr:count="2" thr:updated="2008-11-17T14:40:06-05:00" />
|
115
|
-
<id>tag:typepad.com,2003:post-55147740</id>
|
116
|
-
<published>2008-09-04T16:07:19-04:00</published>
|
117
|
-
<updated>2008-11-17T14:40:06-05:00</updated>
|
118
|
-
<summary>In my previous post about the speed of serializing data, I concluded that Marshal was the quickest way to get things done. So I set about using Marshal to store some data in an ActiveRecord object. Things worked great at...</summary>
|
119
|
-
<author>
|
120
|
-
<name>Paul Dix</name>
|
121
|
-
</author>
|
122
|
-
<category scheme="http://www.sixapart.com/ns/types#category" term="Tahiti" />
|
123
|
-
|
124
|
-
|
125
|
-
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/">
|
126
|
-
<div xmlns="http://www.w3.org/1999/xhtml"><p>In my previous <a href="http://www.pauldix.net/2008/08/serializing-dat.html">post about the speed of serializing data</a>, I concluded that Marshal was the quickest way to get things done. So I set about using Marshal to store some data in an ActiveRecord object. Things worked great at first, but on some test data I got this error: marshal data too short. Luckily, <a href="http://www.brynary.com/">Bryan Helmkamp</a> had helpfully pointed out that there were sometimes problems with storing marshaled data in the database. He said it was best to base64 encode the marshal dump before storing.</p>
|
127
|
-
|
128
|
-
<p>I was curious why it was working on some things and not others. It turns out that some types of data being marshaled were causing the error to pop up. Here's the test data I used in my specs:</p>
|
129
|
-
<pre>{ :foo =&gt; 3, :bar =&gt; 2 } # hash with symbols for keys and integer values<br />[3, 2.1, 4, 8]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; # array with integer and float values</pre>
|
130
|
-
<p>Everything worked when I switched the array values to all integers so it seems that floats were causing the problem. However, in the interest of keeping everything working regardless of data types, I base64 encoded before going into the database and decoded on the way out.</p>
|
131
|
-
|
132
|
-
<p>I also ran the benchmarks again to determine what impact this would have on speed. Here are the results for 100 iterations on a 10k element array and a 10k element hash with and without base64 encode/decode:</p>
|
133
|
-
<pre>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; user&nbsp; &nbsp;&nbsp; &nbsp; system&nbsp; &nbsp;&nbsp; total&nbsp; &nbsp;&nbsp; &nbsp; real<br />array marshal&nbsp; 0.200000&nbsp; &nbsp;0.010000&nbsp; &nbsp;0.210000 (&nbsp; 0.214018) (without Base64)<br />array marshal&nbsp; 0.220000&nbsp; &nbsp;0.010000&nbsp; &nbsp;0.230000 (&nbsp; 0.250260)<br /><br />hash marshal&nbsp; &nbsp;1.830000&nbsp; &nbsp;0.040000&nbsp; &nbsp;1.870000 (&nbsp; 1.892874) (without Base64)<br />hash marshal&nbsp; &nbsp;2.040000&nbsp; &nbsp;0.100000&nbsp; &nbsp;2.140000 (&nbsp; 2.170405)</pre>
|
134
|
-
<p>As you can see the difference in speed is pretty negligible. I assume that the error has to do with AR cleaning the stuff that gets inserted into the database, but I'm not really sure. In the end it's just easier to use Base64.encode64 when serializing data into a text field in ActiveRecord using Marshal.</p>
|
135
|
-
|
136
|
-
<p>I've also read people posting about this error when using the database session store. I can only assume that it's because they were trying to store either way too much data in their session (too much for a regular text field) or they were storing float values or some other data type that would cause this to pop up. Hopefully this helps.</p></div>
|
137
|
-
<div class="feedflare">
|
138
|
-
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=SX7veW.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=SX7veW.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=9RuRMG.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=9RuRMG.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=3a9dsf.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=3a9dsf.p" border="0"></img></a>
|
139
|
-
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/383536354" height="1" width="1"/></content>
|
140
|
-
|
141
|
-
|
142
|
-
<feedburner:origLink>http://www.pauldix.net/2008/09/marshal-data-to.html</feedburner:origLink></entry>
|
143
|
-
<entry>
|
144
|
-
<title>Serializing data speed comparison: Marshal vs. JSON vs. Eval vs. YAML</title>
|
145
|
-
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/376401099/serializing-dat.html" />
|
146
|
-
<link rel="replies" type="text/html" href="http://www.pauldix.net/2008/08/serializing-dat.html" thr:count="5" thr:updated="2008-10-14T01:26:31-04:00" />
|
147
|
-
<id>tag:typepad.com,2003:post-54766774</id>
|
148
|
-
<published>2008-08-27T14:31:41-04:00</published>
|
149
|
-
<updated>2008-10-14T01:26:31-04:00</updated>
|
150
|
-
<summary>Last night at the NYC Ruby hackfest, I got into a discussion about serializing data. Brian mentioned the Marshal library to me, which for some reason had completely escaped my attention until last night. He said it was wicked fast...</summary>
|
151
|
-
<author>
|
152
|
-
<name>Paul Dix</name>
|
153
|
-
</author>
|
154
|
-
<category scheme="http://www.sixapart.com/ns/types#category" term="Tahiti" />
|
155
|
-
|
156
|
-
|
157
|
-
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/">
|
158
|
-
<div xmlns="http://www.w3.org/1999/xhtml"><p>Last night at the <a href="http://nycruby.org">NYC Ruby hackfest</a>, I got into a discussion about serializing data. Brian mentioned the Marshal library to me, which for some reason had completely escaped my attention until last night. He said it was wicked fast so we decided to run a quick benchmark comparison.</p>
|
159
|
-
<p>The test data is designed to roughly approximate what my <a href="http://www.pauldix.net/2008/08/storing-many-cl.html">stored classifier data</a> will look like. The different methods we decided to benchmark were Marshal, json, eval, and yaml. With each one we took the in-memory object and serialized it and then read it back in. With eval we had to convert the object to ruby code to serialize it then run eval against that. Here are the results for 100 iterations on a 10k element array and a hash with 10k key/value pairs run on my Macbook Pro 2.4 GHz Core 2 Duo:</p>
|
160
|
-
<pre>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; user&nbsp; &nbsp;&nbsp; &nbsp;system&nbsp; &nbsp;&nbsp; total&nbsp; &nbsp;&nbsp; &nbsp; real<br />array marshal&nbsp; 0.210000&nbsp; &nbsp;0.010000&nbsp; &nbsp;0.220000 (&nbsp; 0.220701)<br />array json&nbsp; &nbsp;&nbsp; 2.180000&nbsp; &nbsp;0.050000&nbsp; &nbsp;2.230000 (&nbsp; 2.288489)<br />array eval&nbsp; &nbsp;&nbsp; 2.090000&nbsp; &nbsp;0.060000&nbsp; &nbsp;2.150000 (&nbsp; 2.240443)<br />array yaml&nbsp; &nbsp; 26.650000&nbsp; &nbsp;0.350000&nbsp; 27.000000 ( 27.810609)<br /><br />hash marshal&nbsp; &nbsp;2.000000&nbsp; &nbsp;0.050000&nbsp; &nbsp;2.050000 (&nbsp; 2.114950)<br />hash json&nbsp; &nbsp;&nbsp; &nbsp;3.700000&nbsp; &nbsp;0.060000&nbsp; &nbsp;3.760000 (&nbsp; 3.881716)<br />hash eval&nbsp; &nbsp;&nbsp; &nbsp;5.370000&nbsp; &nbsp;0.140000&nbsp; &nbsp;5.510000 (&nbsp; 6.117947)<br />hash yaml&nbsp; &nbsp;&nbsp; 68.220000&nbsp; &nbsp;0.870000&nbsp; 69.090000 ( 72.370784)</pre>
|
161
|
-
<p>The order in which I tested them is pretty much the order in which they ranked for speed. Marshal was amazingly fast. JSON and eval came out roughly equal on the array with eval trailing quite a bit for the hash. Yaml was just slow as all hell. A note on the json: I used the 1.1.3 library which uses c to parse. I assume it would be quite a bit slower if I used the pure ruby implementation. Here's <a href="http://gist.github.com/7549">a gist of the benchmark code</a> if you're curious and want to run it yourself.</p>
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
<p>If you're serializing user data, be super careful about using eval. It's probably best to avoid it completely. Finally, just for fun I took yaml out (it was too slow) and ran the benchmark again with 1k iterations:</p>
|
166
|
-
<pre>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; user&nbsp; &nbsp;&nbsp; &nbsp;system&nbsp; &nbsp;&nbsp; total&nbsp; &nbsp;&nbsp; &nbsp; real<br />array marshal&nbsp; 2.080000&nbsp; &nbsp;0.110000&nbsp; &nbsp;2.190000 (&nbsp; 2.242235)<br />array json&nbsp; &nbsp; 21.860000&nbsp; &nbsp;0.500000&nbsp; 22.360000 ( 23.052403)<br />array eval&nbsp; &nbsp; 20.730000&nbsp; &nbsp;0.570000&nbsp; 21.300000 ( 21.992454)<br /><br />hash marshal&nbsp; 19.510000&nbsp; &nbsp;0.500000&nbsp; 20.010000 ( 20.794111)<br />hash json&nbsp; &nbsp;&nbsp; 39.770000&nbsp; &nbsp;0.670000&nbsp; 40.440000 ( 41.689297)<br />hash eval&nbsp; &nbsp;&nbsp; 51.410000&nbsp; &nbsp;1.290000&nbsp; 52.700000 ( 54.155711)</pre></div>
|
167
|
-
<div class="feedflare">
|
168
|
-
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=a3KCSc.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=a3KCSc.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=zhI5zo.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=zhI5zo.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=u7DqIA.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=u7DqIA.p" border="0"></img></a>
|
169
|
-
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/376401099" height="1" width="1"/></content>
|
170
|
-
|
171
|
-
|
172
|
-
<feedburner:origLink>http://www.pauldix.net/2008/08/serializing-dat.html</feedburner:origLink></entry>
|
173
|
-
|
174
|
-
</feed>
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
|
3
|
+
<title>Paul Dix Explains Nothing</title>
|
4
|
+
|
5
|
+
<link rel="alternate" type="text/html" href="http://www.pauldix.net/" />
|
6
|
+
<id>tag:typepad.com,2003:weblog-108605</id>
|
7
|
+
<updated>2009-01-22T10:50:22-05:00</updated>
|
8
|
+
<subtitle>Entrepreneurship, programming, software development, politics, NYC, and random thoughts.</subtitle>
|
9
|
+
<generator uri="http://www.typepad.com/">TypePad</generator>
|
10
|
+
<link rel="self" href="http://feeds.feedburner.com/PaulDixExplainsNothing" type="application/atom+xml" /><entry>
|
11
|
+
<title>Making a Ruby C library even faster</title>
|
12
|
+
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/519925023/making-a-ruby-c-library-even-faster.html" />
|
13
|
+
<link rel="replies" type="text/html" href="http://www.pauldix.net/2009/01/making-a-ruby-c-library-even-faster.html" thr:count="1" thr:updated="2009-01-22T16:30:53-05:00" />
|
14
|
+
<id>tag:typepad.com,2003:post-61753462</id>
|
15
|
+
<published>2009-01-22T10:50:22-05:00</published>
|
16
|
+
<updated>2009-01-22T16:30:53-05:00</updated>
|
17
|
+
<summary>Last week I released the first version of a SAX based XML parsing library called SAX-Machine. It uses Nokogiri, which uses libxml, so it's pretty fast. However, I felt that it could be even faster. The only question was how...</summary>
|
18
|
+
<author>
|
19
|
+
<name>Paul Dix</name>
|
20
|
+
</author>
|
21
|
+
<category scheme="http://www.sixapart.com/ns/types#category" term="Ruby" />
|
22
|
+
<category scheme="http://www.sixapart.com/ns/types#category" term="Another Category" />
|
23
|
+
|
24
|
+
|
25
|
+
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/"><p>Last week I released the first version of a <a href="http://www.pauldix.net/2009/01/sax-machine-sax-parsing-made-easy.html">SAX based XML parsing library called SAX-Machine</a>. It uses Nokogiri, which uses libxml, so it's pretty fast. However, I felt that it could be even faster. The only question was how to make a ruby library that is already using c underneath perform better. Since I've never written a Ruby C extension and it's been a few years since I've touched C, I decided it would be a good educational experience to give it a try.</p>
|
26
|
+
|
27
|
+
<p>First, let's look into how Nokogiri and SAX-Machine perform a parse. The syntax for SAX-Machine builds up a set of class variables (actually, instance variables on a class object) that describe what you're interested in parsing. So when you see something like this:
|
28
|
+
</p><script src="http://gist.github.com/50549.js"></script><p>
|
29
|
+
It calls the 'element' and 'elements' methods inserted by the SAXMachine module that build up ruby objects that describe what XML tags we're interested in for the Entry class. That's all pretty straight forward and not really the source of any slowdown in the parsing process. These calls only happen once, when you first load the class.
|
30
|
+
|
31
|
+
</p><p>Things get interesting when you run a parse. So you run Entry.parse(some_xml). That makes the call to Nokogiri, which in turn makes a call to libxml. Libxml then parses over the stream (or string) and makes calls to C methods (in Nokogiri) on certain events. For our purposes, the most interesting are start_element, end_element, and characters_func. The C code in Nokogiri for these is basic. It simply converts those C variables into Ruby ones and then makes calls to whatever instance of Nokogiri::XML:SAX::Document (a Ruby object) is associated with this parse. This is where SAXMachine comes back in. It has handlers for these events that match up the tags with the previously defined SAXMachine objects attached to the Entry class. It ignores the events that don't match a tag (however, it still needs to determine if the tag should be ignored).</p>
|
32
|
+
|
33
|
+
<p>The only possible place I saw to speed things up was to push more of SAX event handling down into the C code. Unfortunately, the only way to do this was to abandon Nokogiri and write my own code to interface with libxml. I used the xml_sax_parser.c from Nokogiri as a base and added to it. I changed it so the SAXMachine definitions of what was interesting would be stored in C. I then changed the SAX handling code to capture the events in C and determine if a tag was of interest there before sending it off to the Ruby objects. The end result is that calls are only made to Ruby when there is an actual event of interest. Thus, I avoid doing any comparisons in Ruby and those classes are simply wrappers that call out to the correct value setters.</p>
|
34
|
+
|
35
|
+
<p>Here are the results of a quick speed comparison against the Nokogiri SAXMachine, parsing my atom feed using <a href="http://gist.github.com/47938">code from my last post</a>.</p>
|
36
|
+
<pre> user system total real<br>sax c 0.060000 0.000000 0.060000 ( 0.069990)<br>sax nokogiri 0.500000 0.010000 0.510000 ( 0.520278)<br></pre><p>
|
37
|
+
The SAX C is 7.4 times faster than SAX Nokogiri. Now, that doesn't seem like a whole lot, but I think it's quite good considering it was against a library that was already half in C. It's even more punctuated when you look at the comparison of these two against rfeedparser.
|
38
|
+
</p><pre> user system total real<br>sax c 0.060000 0.000000 0.060000 ( 0.069990)<br>sax nokogiri 0.500000 0.010000 0.510000 ( 0.520278)<br>rfeedparser 13.770000 1.730000 15.500000 ( 15.690309)<br></pre>
|
39
|
+
<p>The SAX C version is 224 times faster than rfeedparser! The 7 times multiple from the Nokogiri version of SAXMachine really makes a difference. Unfortunately, I really only wrote this code as a test. It's not even close to something I would use for real. It has memory leaks, isn't thread safe, is completely unreadable, and has hidden bugs that I know about. You can take a look at it in all its misery on the <a href="http://github.com/pauldix/sax-machine/tree/c-refactor">c-rafactor branch of SAXMachine on github</a>. Even though the code is awful, I think it's interesting that there can be this much variability in performance on Ruby libraries that are using C.</p>
|
40
|
+
|
41
|
+
<p>I could actually turn this into a legitimate working version, but it would take more work than I think it's worth at this point. Also, I'm not excited about the idea of dealing with C issues in SAXMachine. I would be more excited for it if I could get this type of SAX parsing thing into Nokogiri (in addition to the one that is there now). For now, I'll move on to using the Nokogiri version of SAXMachine to create a feed parsing library.</p><div class="feedflare">
|
42
|
+
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=9Q8qfQ.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=9Q8qfQ.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=rLK96Z.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=rLK96Z.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=B95sFg.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=B95sFg.p" border="0"></img></a>
|
43
|
+
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/519925023" height="1" width="1"/></content>
|
44
|
+
|
45
|
+
<wfw:commentRss>this is the new val</wfw:commentRss>
|
46
|
+
<feedburner:origLink>http://www.pauldix.net/2009/01/making-a-ruby-c-library-even-faster.html</feedburner:origLink></entry>
|
47
|
+
<entry>
|
48
|
+
<title>SAX Machine - SAX Parsing made easy</title>
|
49
|
+
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/514044516/sax-machine-sax-parsing-made-easy.html" />
|
50
|
+
<link rel="replies" type="text/html" href="http://www.pauldix.net/2009/01/sax-machine-sax-parsing-made-easy.html" thr:count="4" thr:updated="2009-01-21T23:16:44-05:00" />
|
51
|
+
<id>tag:typepad.com,2003:post-61473064</id>
|
52
|
+
<published>2009-01-16T09:47:32-05:00</published>
|
53
|
+
<updated>2009-01-21T23:16:44-05:00</updated>
|
54
|
+
<summary>For a while now I've been wanting to create a ruby library for feed (Atom, RSS) parsing. I know there are already some out there, but for one reason or another each of them falls short for my needs. The...</summary>
|
55
|
+
<author>
|
56
|
+
<name>Paul Dix</name>
|
57
|
+
</author>
|
58
|
+
<category scheme="http://www.sixapart.com/ns/types#category" term="Ruby" />
|
59
|
+
|
60
|
+
|
61
|
+
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/"><p>For a while now I've been wanting to create a ruby library for feed (Atom, RSS) parsing. I know there are already some out there, but for one reason or another each of them falls short for my needs. The first part of this effort requires me to decide how I want to go about parsing the feeds. One of the primary goals is speed. I want this thing to be fast. So I decided to look into SAX parsing. Further, I wanted to use Nokogiri because I'm going to be doing a few other things with the posts like pulling out links and tags, and sanitizing entries.</p><p>SAX parsing is an event based parsing model. It fires events during parsing for things like start_tag, end_tag, start_document, end_document, and characters. Nokogiri contains a very light weight wrapper around libxml's SAX parsing. Using this I started creating a basic declarative syntax for registering parsing events and mapping them into objects and values. The end result is <a href="http://github.com/pauldix/sax-machine">SAX Machine, a declarative SAX parser for parsing xml into ruby objects</a>.</p><p>Some of the syntax was inspired by <a href="http://github.com/jnunemaker/happymapper">John Nunemaker's Happy Mapper</a>. However, the behavior is a bit different. There are probably many more features I am going to add in as I use it to create my feed parsing library, but the basic stuff is there right now.</p><p>Here's an example that uses SAX Machine to parse a FeedBurner atom feed into ruby objects.</p><script src="http://gist.github.com/47938.js"></script><p>
|
62
|
+
|
63
|
+
So in a few short lines of code this parses feedburner feeds exactly how I want them. It's also pretty fast. Here's a benchmark against rfeedparser running against the atom feed from this blog 100 times.
|
64
|
+
</p><pre>feedzirra 1.430000 0.020000 1.450000 ( 1.472081)<br>rfeedparser 15.780000 0.580000 16.360000 ( 16.768889)<br></pre><p>
|
65
|
+
It's about 11 times faster in this test. I have to do more tests before I feel comfortable with that figure, but it's a promising start. More importantly this will enable me to make a feed parser that's flexible, extensible, and readable.</p><p>SAX Machine is available as a gem as long as you have github added as a gem source. Just do gem install pauldix-sax-machine to get the party started (<strong>UPDATE:</strong> for some reason it's not built yet. looking into it. <strong>UPDATE 2: </strong>it's built and ready to go. thanks to nakajima). Also, special thanks to <a href="http://www.brynary.com/">Bryan Helmkamp</a> for helping me turn my initial spike into a decently tested and refactored version (even if it did slow my benchmark down by a factor of 4).</p><div class="feedflare">
|
66
|
+
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=2VN4Om.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=2VN4Om.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=5MHPvx.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=5MHPvx.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=nMpoB4.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=nMpoB4.p" border="0"></img></a>
|
67
|
+
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/514044516" height="1" width="1"/></content>
|
68
|
+
|
69
|
+
|
70
|
+
<feedburner:origLink>http://www.pauldix.net/2009/01/sax-machine-sax-parsing-made-easy.html</feedburner:origLink></entry>
|
71
|
+
<entry>
|
72
|
+
<title>Professional Goals for 2009</title>
|
73
|
+
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/499546917/professional-goals-for-2009.html" />
|
74
|
+
<link rel="replies" type="text/html" href="http://www.pauldix.net/2008/12/professional-goals-for-2009.html" thr:count="3" thr:updated="2009-01-01T10:17:04-05:00" />
|
75
|
+
<id>tag:typepad.com,2003:post-60634768</id>
|
76
|
+
<published>2008-12-31T10:55:02-05:00</published>
|
77
|
+
<updated>2009-01-01T10:17:05-05:00</updated>
|
78
|
+
<summary>In two weeks I'll be making the transition from full time student to full time worker. Instead of worrying about grades and papers I'll have to get real work done. For the last four years I've set a lot of...</summary>
|
79
|
+
<author>
|
80
|
+
<name>Paul Dix</name>
|
81
|
+
</author>
|
82
|
+
|
83
|
+
|
84
|
+
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/"><p>In two weeks I'll be making the transition from full time student to full time worker. Instead of worrying about grades and papers I'll have to get real work done. For the last four years I've set a lot of goals for myself, but I've mainly been focused on the single goal of finishing school. Now that I've accomplished that it's time to figure out what the next steps are. Being that it's the eve of a new year, I thought a good way to start would be to outline exactly what I'd like to accomplish professionally in 2009. So here are some goals in no particular order:
|
85
|
+
</p><ul>
|
86
|
+
<li>Write 48 well thought out blog posts (once a week for 48 weeks of the year)</li>
|
87
|
+
<li>Write an open source library that at least 2 people/organizations use in production</li>
|
88
|
+
<li>Contribute to a popular open source library/project</li>
|
89
|
+
<li>Finish a working version of <a href="http://www.pauldix.net/tahiti/index.html">Filterly</a> and use it daily</li>
|
90
|
+
<li>Learn a new programming language</li>
|
91
|
+
<li>Finish reading <a href="http://www.amazon.com/Introduction-Machine-Learning-Adaptive-Computation/dp/0262012111/ref=sr_1_3?ie=UTF8&amp;s=books&amp;qid=1230736254&amp;sr=1-3">this book on machine learning</a></li>
|
92
|
+
<li>Read <a href="http://www.amazon.com/Learning-Kernels-Regularization-Optimization-Computation/dp/0262194759/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1230736307&amp;sr=1-1">Learning With Kernels by Schlkopf and Smola</a></li>
|
93
|
+
<li>Be a contributing author to a book or shortcut</li>
|
94
|
+
<li>Present at a conference</li>
|
95
|
+
<li>Present at a users group</li>
|
96
|
+
<li>Help create a site that gets decent traffic (kind of non-specific, I know)
|
97
|
+
</li>
|
98
|
+
<li>Attend two conferences</li>
|
99
|
+
<li>Attend at least 8 <a href="http://nycruby.org/wiki/">nyc.rb (NYC Ruby) meetings</a></li>
|
100
|
+
<li>Attend at least 6 <a href="http://www.meetup.com/ny-tech/">NY Tech Meetups</a></li>
|
101
|
+
<li>Attend at least 4 <a href="http://www.meetup.com/BlueVC/">Columbia Blue Venture Community events</a> (hard because it conflicts with nyc.rb)</li>
|
102
|
+
<li>Attend at least 4 <a href="http://nextny.org/">NextNY</a> events</li>
|
103
|
+
<li>Do all the <a href="http://codekata.pragprog.com/">Code Katas</a> at least once (any language)</li>
|
104
|
+
</ul>
|
105
|
+
<p>That's all I've come up with so far. I probably won't be able to get everything on the list, but if I get most of the way there I'll be happy. A year may be too long of a development cycle for professional goals. It would be much more agile if I broke these down into two week sprints, but this will do for now. It gives me specific areas to focus my efforts over the next 12 months. What goals do you have for 2009?</p><div class="feedflare">
|
106
|
+
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=IP49Wo.O"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=IP49Wo.O" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=4Azfxm.o"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=4Azfxm.o" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=iwU8iG.o"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=iwU8iG.o" border="0"></img></a>
|
107
|
+
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/499546917" height="1" width="1"/></content>
|
108
|
+
|
109
|
+
|
110
|
+
<feedburner:origLink>http://www.pauldix.net/2008/12/professional-goals-for-2009.html</feedburner:origLink></entry>
|
111
|
+
<entry>
|
112
|
+
<title>Marshal data too short error with ActiveRecord</title>
|
113
|
+
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/383536354/marshal-data-to.html" />
|
114
|
+
<link rel="replies" type="text/html" href="http://www.pauldix.net/2008/09/marshal-data-to.html" thr:count="2" thr:updated="2008-11-17T14:40:06-05:00" />
|
115
|
+
<id>tag:typepad.com,2003:post-55147740</id>
|
116
|
+
<published>2008-09-04T16:07:19-04:00</published>
|
117
|
+
<updated>2008-11-17T14:40:06-05:00</updated>
|
118
|
+
<summary>In my previous post about the speed of serializing data, I concluded that Marshal was the quickest way to get things done. So I set about using Marshal to store some data in an ActiveRecord object. Things worked great at...</summary>
|
119
|
+
<author>
|
120
|
+
<name>Paul Dix</name>
|
121
|
+
</author>
|
122
|
+
<category scheme="http://www.sixapart.com/ns/types#category" term="Tahiti" />
|
123
|
+
|
124
|
+
|
125
|
+
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/">
|
126
|
+
<div xmlns="http://www.w3.org/1999/xhtml"><p>In my previous <a href="http://www.pauldix.net/2008/08/serializing-dat.html">post about the speed of serializing data</a>, I concluded that Marshal was the quickest way to get things done. So I set about using Marshal to store some data in an ActiveRecord object. Things worked great at first, but on some test data I got this error: marshal data too short. Luckily, <a href="http://www.brynary.com/">Bryan Helmkamp</a> had helpfully pointed out that there were sometimes problems with storing marshaled data in the database. He said it was best to base64 encode the marshal dump before storing.</p>
|
127
|
+
|
128
|
+
<p>I was curious why it was working on some things and not others. It turns out that some types of data being marshaled were causing the error to pop up. Here's the test data I used in my specs:</p>
|
129
|
+
<pre>{ :foo =&gt; 3, :bar =&gt; 2 } # hash with symbols for keys and integer values<br />[3, 2.1, 4, 8]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; # array with integer and float values</pre>
|
130
|
+
<p>Everything worked when I switched the array values to all integers so it seems that floats were causing the problem. However, in the interest of keeping everything working regardless of data types, I base64 encoded before going into the database and decoded on the way out.</p>
|
131
|
+
|
132
|
+
<p>I also ran the benchmarks again to determine what impact this would have on speed. Here are the results for 100 iterations on a 10k element array and a 10k element hash with and without base64 encode/decode:</p>
|
133
|
+
<pre>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; user&nbsp; &nbsp;&nbsp; &nbsp; system&nbsp; &nbsp;&nbsp; total&nbsp; &nbsp;&nbsp; &nbsp; real<br />array marshal&nbsp; 0.200000&nbsp; &nbsp;0.010000&nbsp; &nbsp;0.210000 (&nbsp; 0.214018) (without Base64)<br />array marshal&nbsp; 0.220000&nbsp; &nbsp;0.010000&nbsp; &nbsp;0.230000 (&nbsp; 0.250260)<br /><br />hash marshal&nbsp; &nbsp;1.830000&nbsp; &nbsp;0.040000&nbsp; &nbsp;1.870000 (&nbsp; 1.892874) (without Base64)<br />hash marshal&nbsp; &nbsp;2.040000&nbsp; &nbsp;0.100000&nbsp; &nbsp;2.140000 (&nbsp; 2.170405)</pre>
|
134
|
+
<p>As you can see the difference in speed is pretty negligible. I assume that the error has to do with AR cleaning the stuff that gets inserted into the database, but I'm not really sure. In the end it's just easier to use Base64.encode64 when serializing data into a text field in ActiveRecord using Marshal.</p>
|
135
|
+
|
136
|
+
<p>I've also read people posting about this error when using the database session store. I can only assume that it's because they were trying to store either way too much data in their session (too much for a regular text field) or they were storing float values or some other data type that would cause this to pop up. Hopefully this helps.</p></div>
|
137
|
+
<div class="feedflare">
|
138
|
+
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=SX7veW.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=SX7veW.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=9RuRMG.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=9RuRMG.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=3a9dsf.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=3a9dsf.p" border="0"></img></a>
|
139
|
+
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/383536354" height="1" width="1"/></content>
|
140
|
+
|
141
|
+
|
142
|
+
<feedburner:origLink>http://www.pauldix.net/2008/09/marshal-data-to.html</feedburner:origLink></entry>
|
143
|
+
<entry>
|
144
|
+
<title>Serializing data speed comparison: Marshal vs. JSON vs. Eval vs. YAML</title>
|
145
|
+
<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/376401099/serializing-dat.html" />
|
146
|
+
<link rel="replies" type="text/html" href="http://www.pauldix.net/2008/08/serializing-dat.html" thr:count="5" thr:updated="2008-10-14T01:26:31-04:00" />
|
147
|
+
<id>tag:typepad.com,2003:post-54766774</id>
|
148
|
+
<published>2008-08-27T14:31:41-04:00</published>
|
149
|
+
<updated>2008-10-14T01:26:31-04:00</updated>
|
150
|
+
<summary>Last night at the NYC Ruby hackfest, I got into a discussion about serializing data. Brian mentioned the Marshal library to me, which for some reason had completely escaped my attention until last night. He said it was wicked fast...</summary>
|
151
|
+
<author>
|
152
|
+
<name>Paul Dix</name>
|
153
|
+
</author>
|
154
|
+
<category scheme="http://www.sixapart.com/ns/types#category" term="Tahiti" />
|
155
|
+
|
156
|
+
|
157
|
+
<content type="html" xml:lang="en-US" xml:base="http://www.pauldix.net/">
|
158
|
+
<div xmlns="http://www.w3.org/1999/xhtml"><p>Last night at the <a href="http://nycruby.org">NYC Ruby hackfest</a>, I got into a discussion about serializing data. Brian mentioned the Marshal library to me, which for some reason had completely escaped my attention until last night. He said it was wicked fast so we decided to run a quick benchmark comparison.</p>
|
159
|
+
<p>The test data is designed to roughly approximate what my <a href="http://www.pauldix.net/2008/08/storing-many-cl.html">stored classifier data</a> will look like. The different methods we decided to benchmark were Marshal, json, eval, and yaml. With each one we took the in-memory object and serialized it and then read it back in. With eval we had to convert the object to ruby code to serialize it then run eval against that. Here are the results for 100 iterations on a 10k element array and a hash with 10k key/value pairs run on my Macbook Pro 2.4 GHz Core 2 Duo:</p>
|
160
|
+
<pre>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; user&nbsp; &nbsp;&nbsp; &nbsp;system&nbsp; &nbsp;&nbsp; total&nbsp; &nbsp;&nbsp; &nbsp; real<br />array marshal&nbsp; 0.210000&nbsp; &nbsp;0.010000&nbsp; &nbsp;0.220000 (&nbsp; 0.220701)<br />array json&nbsp; &nbsp;&nbsp; 2.180000&nbsp; &nbsp;0.050000&nbsp; &nbsp;2.230000 (&nbsp; 2.288489)<br />array eval&nbsp; &nbsp;&nbsp; 2.090000&nbsp; &nbsp;0.060000&nbsp; &nbsp;2.150000 (&nbsp; 2.240443)<br />array yaml&nbsp; &nbsp; 26.650000&nbsp; &nbsp;0.350000&nbsp; 27.000000 ( 27.810609)<br /><br />hash marshal&nbsp; &nbsp;2.000000&nbsp; &nbsp;0.050000&nbsp; &nbsp;2.050000 (&nbsp; 2.114950)<br />hash json&nbsp; &nbsp;&nbsp; &nbsp;3.700000&nbsp; &nbsp;0.060000&nbsp; &nbsp;3.760000 (&nbsp; 3.881716)<br />hash eval&nbsp; &nbsp;&nbsp; &nbsp;5.370000&nbsp; &nbsp;0.140000&nbsp; &nbsp;5.510000 (&nbsp; 6.117947)<br />hash yaml&nbsp; &nbsp;&nbsp; 68.220000&nbsp; &nbsp;0.870000&nbsp; 69.090000 ( 72.370784)</pre>
|
161
|
+
<p>The order in which I tested them is pretty much the order in which they ranked for speed. Marshal was amazingly fast. JSON and eval came out roughly equal on the array with eval trailing quite a bit for the hash. Yaml was just slow as all hell. A note on the json: I used the 1.1.3 library which uses c to parse. I assume it would be quite a bit slower if I used the pure ruby implementation. Here's <a href="http://gist.github.com/7549">a gist of the benchmark code</a> if you're curious and want to run it yourself.</p>
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
<p>If you're serializing user data, be super careful about using eval. It's probably best to avoid it completely. Finally, just for fun I took yaml out (it was too slow) and ran the benchmark again with 1k iterations:</p>
|
166
|
+
<pre>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; user&nbsp; &nbsp;&nbsp; &nbsp;system&nbsp; &nbsp;&nbsp; total&nbsp; &nbsp;&nbsp; &nbsp; real<br />array marshal&nbsp; 2.080000&nbsp; &nbsp;0.110000&nbsp; &nbsp;2.190000 (&nbsp; 2.242235)<br />array json&nbsp; &nbsp; 21.860000&nbsp; &nbsp;0.500000&nbsp; 22.360000 ( 23.052403)<br />array eval&nbsp; &nbsp; 20.730000&nbsp; &nbsp;0.570000&nbsp; 21.300000 ( 21.992454)<br /><br />hash marshal&nbsp; 19.510000&nbsp; &nbsp;0.500000&nbsp; 20.010000 ( 20.794111)<br />hash json&nbsp; &nbsp;&nbsp; 39.770000&nbsp; &nbsp;0.670000&nbsp; 40.440000 ( 41.689297)<br />hash eval&nbsp; &nbsp;&nbsp; 51.410000&nbsp; &nbsp;1.290000&nbsp; 52.700000 ( 54.155711)</pre></div>
|
167
|
+
<div class="feedflare">
|
168
|
+
<a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=a3KCSc.P"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=a3KCSc.P" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=zhI5zo.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=zhI5zo.p" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?a=u7DqIA.p"><img src="http://feeds.feedburner.com/~f/PaulDixExplainsNothing?i=u7DqIA.p" border="0"></img></a>
|
169
|
+
</div><img src="http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~4/376401099" height="1" width="1"/></content>
|
170
|
+
|
171
|
+
|
172
|
+
<feedburner:origLink>http://www.pauldix.net/2008/08/serializing-dat.html</feedburner:origLink></entry>
|
173
|
+
|
174
|
+
</feed>
|
@@ -16,10 +16,20 @@
|
|
16
16
|
<pubDate>Mon, 29 Dec 2008 07:51:19 +0000</pubDate>
|
17
17
|
<generator>http://wordpress.org/?v=2.7</generator>
|
18
18
|
<language>en</language>
|
19
|
+
<lastBuildDate>Sat, 07 Sep 2002 09:42:31 GMT</lastBuildDate>
|
20
|
+
<ttl>60</ttl>
|
21
|
+
<image>
|
22
|
+
<url>https://tenderlovemaking.com/images/header-logo-text-trimmed.png</url>
|
23
|
+
<title>Tender Lovemaking</title>
|
24
|
+
<link>http://tenderlovemaking.com</link>
|
25
|
+
<width>766</width>
|
26
|
+
<height>138</height>
|
27
|
+
<description>The act of making love, tenderly.</description>
|
28
|
+
</image>
|
19
29
|
<sy:updatePeriod>hourly</sy:updatePeriod>
|
20
30
|
<sy:updateFrequency>1</sy:updateFrequency>
|
21
|
-
|
22
|
-
|
31
|
+
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com/"/>
|
32
|
+
<item>
|
23
33
|
<title>Nokogiri’s Slop Feature</title>
|
24
34
|
<link>http://tenderlovemaking.com/2008/12/04/nokogiris-slop-feature/</link>
|
25
35
|
<comments>http://tenderlovemaking.com/2008/12/04/nokogiris-slop-feature/#comments</comments>
|
@@ -1,611 +1,611 @@
|
|
1
|
-
|
2
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
3
|
-
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-US">
|
4
|
-
<title>TrotterCashion - Home</title>
|
5
|
-
<id>tag:trottercashion.com,2008:mephisto/</id>
|
6
|
-
<generator uri="http://mephistoblog.com" version="0.8.0">Mephisto Drax</generator>
|
7
|
-
|
8
|
-
<link href="http://trottercashion.com/" rel="alternate" type="text/html" />
|
9
|
-
<updated>2008-12-13T17:48:58Z</updated>
|
10
|
-
<link rel="self" href="http://feeds.feedburner.com/trottercashion" type="application/atom+xml" /><entry xml:base="http://trottercashion.com/">
|
11
|
-
<author>
|
12
|
-
<name>trotter</name>
|
13
|
-
</author>
|
14
|
-
<id>tag:trottercashion.com,2008-12-13:53</id>
|
15
|
-
<published>2008-12-13T17:43:00Z</published>
|
16
|
-
<updated>2008-12-13T17:48:58Z</updated>
|
17
|
-
<category term="dataurls" />
|
18
|
-
<category term="javascript" />
|
19
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/483840026/data-urls-and-document-domain" rel="alternate" type="text/html" />
|
20
|
-
<title>Data Urls and document.domain</title>
|
21
|
-
<content type="html">
|
22
|
-
<p>Well this is a bummer. It turns out that all data urls share a common domain of ””. This is a problem in <span class="caps">HTML5</span>, because access to sqlite databases is based on the document.domain (This is true in safari at least). Therefore, all data urls will share a common sqlite db environment, meaning that a data url from Google,could look in the database created by a Yahoo data url, given that they were able to guess the name of the database. Since I see data urls as a better way to do offline web apps than google gears, this is a problem that pains me. Does anyone know if there is a solution?</p>
|
23
|
-
|
24
|
-
|
25
|
-
<p>My main thought on how to fix this would be to require the domain for any data url that is a target of an link be set to the domain of the linker. The same would go for any data url that is loaded via a src=””, but this shouldn’t matter as all scripts use the document domain and not their own domain for security purposes. In cases where this is no linker, data urls get their domain set to an md5 hash of their data. Anyone see any problems with this solution?</p>
|
26
|
-
|
27
|
-
|
28
|
-
<p>If you don’t know what data urls are, check out my <a href="http://trottercashion.com/2008/12/12/data-urls-are-fun">previous post</a>.</p>
|
29
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/12/13/data-urls-and-document-domain</feedburner:origLink></entry>
|
30
|
-
<entry xml:base="http://trottercashion.com/">
|
31
|
-
<author>
|
32
|
-
<name>trotter</name>
|
33
|
-
</author>
|
34
|
-
<id>tag:trottercashion.com,2008-12-12:48</id>
|
35
|
-
<published>2008-12-12T13:03:00Z</published>
|
36
|
-
<updated>2008-12-12T13:04:13Z</updated>
|
37
|
-
<category term="dataurls" />
|
38
|
-
<category term="iphone" />
|
39
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/482664882/data-urls-are-fun" rel="alternate" type="text/html" />
|
40
|
-
<title>Data Urls Are Fun!</title>
|
41
|
-
<content type="html">
|
42
|
-
<p>Lately I’ve been playing with data urls in an effort to use them as an alternative way to build iPhone apps. W. Clawpaws wrote an interesting <a href="http://blog.clawpaws.net/post/2007/07/16/Storing-iPhone-apps-locally-with-data-URLs">post</a> on this a year ago, but it seems that not much has been done since. If it’s possible, I plan to have a simple library built by end of year that will allow you to write data url apps that connect to a central server when available. Basically, it’ll make the persistence and speed arguments for writing native apps null and void.</p>
|
43
|
-
|
44
|
-
|
45
|
-
<p>Anyway, enough of what I plan to do and more about actual data urls. “Data urls”http://en.wikipedia.org/wiki/Data_URI_scheme allow you to store a single image, some javascript, or even an entire web page in a url. The browser will then render that information as if it were pulling it from a normal http:// url. So they will increase the initial payload of your web page, but result in faster interactions once the page is loaded.</p>
|
46
|
-
|
47
|
-
|
48
|
-
<p>Data urls have a format of</p>
|
49
|
-
|
50
|
-
|
51
|
-
<pre>data:[&lt;MIME-type&gt;][;charset="&lt;encoding&gt;"][;base64],&lt;data&gt;</pre>
|
52
|
-
|
53
|
-
<p>So if you see something like data:text/html;base64,PGh0bWw+, you’re looking at a data url. To see one in action, click the link below and have a look at the url in your navigation bar.</p>
|
54
|
-
|
55
|
-
|
56
|
-
<p>
|
57
|
-
<a>Data url in action!</a>
|
58
|
-
</p>
|
59
|
-
|
60
|
-
<p>Check back in the near future for progress on the iPhone idea. In the meantime, enjoy playing with data urls.</p>
|
61
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/12/12/data-urls-are-fun</feedburner:origLink></entry>
|
62
|
-
<entry xml:base="http://trottercashion.com/">
|
63
|
-
<author>
|
64
|
-
<name>trotter</name>
|
65
|
-
</author>
|
66
|
-
<id>tag:trottercashion.com,2008-12-11:44</id>
|
67
|
-
<published>2008-12-11T14:48:00Z</published>
|
68
|
-
<updated>2008-12-11T14:49:22Z</updated>
|
69
|
-
<category term="iphone" />
|
70
|
-
<category term="rails" />
|
71
|
-
<category term="speaking" />
|
72
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/481739351/iphone-on-rails-the-presentation" rel="alternate" type="text/html" />
|
73
|
-
<title>iPhone on Rails: The Presentation</title>
|
74
|
-
<content type="html">
|
75
|
-
<p>Last night’s presentation went pretty well. I gave a fairly high level (except for the XCode part) overview of developing web and native apps for the iPhone. I hope to spruce this one up based on the questions asked during the talk last night and give it again some place else. Anyway, slides are below.</p>
|
76
|
-
|
77
|
-
|
78
|
-
<div><a href="http://www.slideshare.net/trotter/iphonerails-presentation?type=powerpoint" title="iPhone on Rails">iPhone on Rails</a>&lt;object height="355" width="425">&lt;param />&lt;param />&lt;param />&lt;embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=iphonerails-1229006573528763-1&amp;#38;stripped_title=iphonerails-presentation" height="355" width="425">&lt;/embed>&lt;/object><div>View SlideShare <a href="http://www.slideshare.net/trotter/iphonerails-presentation?type=powerpoint" title="View iPhone on Rails on SlideShare">presentation</a> or <a href="http://www.slideshare.net/upload?type=powerpoint">Upload</a> your own. (tags: <a href="http://slideshare.net/tag/rails">rails</a> <a href="http://slideshare.net/tag/iphone">iphone</a>)</div></div>
|
79
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/12/11/iphone-on-rails-the-presentation</feedburner:origLink></entry>
|
80
|
-
<entry xml:base="http://trottercashion.com/">
|
81
|
-
<author>
|
82
|
-
<name>trotter</name>
|
83
|
-
</author>
|
84
|
-
<id>tag:trottercashion.com,2008-12-10:43</id>
|
85
|
-
<published>2008-12-10T16:17:00Z</published>
|
86
|
-
<updated>2008-12-10T16:17:37Z</updated>
|
87
|
-
<category term="iphone" />
|
88
|
-
<category term="speaking" />
|
89
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/480717107/iphone-on-rails" rel="alternate" type="text/html" />
|
90
|
-
<title>iPhone on Rails</title>
|
91
|
-
<content type="html">
|
92
|
-
<p>I’m a little late to this party, but I’m speaking at <a href="http://www.phillyonrails.org/events/3">philly.rb</a> tonight. The talk is about making rails backed iPhone apps, both web and native. It should be a raucous good time, so come on over if you’re near Philly.</p>
|
93
|
-
|
94
|
-
|
95
|
-
<p>As an aside, there’s a chance that this will be taped or that I’ll actually upload my slides for once. That said, if you miss the talk, you’re probably shit out of luck for my valuable knowledge.</p>
|
96
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/12/10/iphone-on-rails</feedburner:origLink></entry>
|
97
|
-
<entry xml:base="http://trottercashion.com/">
|
98
|
-
<author>
|
99
|
-
<name>trotter</name>
|
100
|
-
</author>
|
101
|
-
<id>tag:trottercashion.com,2008-10-01:39</id>
|
102
|
-
<published>2008-10-01T23:17:00Z</published>
|
103
|
-
<updated>2008-10-01T23:39:57Z</updated>
|
104
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/408719167/iphone-resources" rel="alternate" type="text/html" />
|
105
|
-
<title>iPhone Resources</title>
|
106
|
-
<content type="html">
|
107
|
-
<p>I’m sure you’ve heard by now that the <a href="http://www.tuaw.com/2008/10/01/iphone-nda-dropped/">iPhone <span class="caps">NDA</span> has been lifted</a>. This is great news for those of us that pretend to be iPhone developers, because we’re going to start seeing a lot more resources at our fingertips.</p>
|
108
|
-
|
109
|
-
|
110
|
-
<p>I’m going to start keeping a moderated page of <a href="http://trottercashion.com/iphone-development">iPhone development resources</a> including links to blog posts, books, and maybe even podcasts. If you see any blog posts (I’m sure there’ll be tons in the next few days). Let me know and I’ll add them to the list.</p>
|
111
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/10/1/iphone-resources</feedburner:origLink></entry>
|
112
|
-
<entry xml:base="http://trottercashion.com/">
|
113
|
-
<author>
|
114
|
-
<name>trotter</name>
|
115
|
-
</author>
|
116
|
-
<id>tag:trottercashion.com,2008-10-01:38</id>
|
117
|
-
<published>2008-10-01T23:10:00Z</published>
|
118
|
-
<updated>2008-10-01T23:11:49Z</updated>
|
119
|
-
<category term="javascript" />
|
120
|
-
<category term="rspec" />
|
121
|
-
<category term="screw-unit" />
|
122
|
-
<category term="testing" />
|
123
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/408699804/mocking-screw-unit-part-deux" rel="alternate" type="text/html" />
|
124
|
-
<title>Mocking Screw-Unit Part Deux</title>
|
125
|
-
<content type="html">
|
126
|
-
<p>I <a href="http://trottercashion.com/2008/8/8/mocking-screw-unit">wrote earlier</a> about how <a href="http://github.com/tobowers">Topper</a> <a href="http://blog.toppingdesign.com/2008/07/22/screw-unit-javascript-mocking/">mocked out the dom</a> for screw-unit testing. Taking his lead, I started playing with screw-unit and adding some mocking and stubbing in the <a href="http://rspec.info">rspec</a> way. It’s not quite release worthy, but it’s on github now and I think it’s nearly usable. Basically, it lets you do things like this:</p>
|
127
|
-
|
128
|
-
|
129
|
-
<pre class="javascript">
|
130
|
-
user = {login: 'bob'};
|
131
|
-
Screw.Stub.stub(user, 'login').andReturn('nancy');
|
132
|
-
user.login; // =&gt; 'nancy'
|
133
|
-
Screw.Stub.reset(); // Called automatically after each spec
|
134
|
-
user.login; // =&gt; 'bob'
|
135
|
-
|
136
|
-
// Will throw a spec failure if user.email() is never called.
|
137
|
-
Screw.stub.shouldReceive(user, 'email');
|
138
|
-
</pre>
|
139
|
-
|
140
|
-
<p>Obviously shouldReceive is not quite complete. It’s missing with(), numberOfTimes(), and other things. Still, it’s good enough that others can start iterating on the model I’ve laid down. As I said earlier, my <a href="http://github.com/trotter/screw-unit/tree/master">fork of screw-unit</a> is available on github now, so have a look and feel free to leave questions in the comments.</p>
|
141
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/10/1/mocking-screw-unit-part-deux</feedburner:origLink></entry>
|
142
|
-
<entry xml:base="http://trottercashion.com/">
|
143
|
-
<author>
|
144
|
-
<name>trotter</name>
|
145
|
-
</author>
|
146
|
-
<id>tag:trottercashion.com,2008-09-27:33</id>
|
147
|
-
<published>2008-09-27T23:33:00Z</published>
|
148
|
-
<updated>2008-10-14T14:10:17Z</updated>
|
149
|
-
<category term="bugs" />
|
150
|
-
<category term="git" />
|
151
|
-
<category term="git-bisect" />
|
152
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/405022957/git-bisect-is-your-new-best-friend" rel="alternate" type="text/html" />
|
153
|
-
<title>git-bisect Is Your New Best Friend</title>
|
154
|
-
<content type="html">
|
155
|
-
<p>To anyone not using <a href="http://en.wikipedia.org/wiki/Git_(software)">git</a>, jump to the bottom of the post then come back up.</p>
|
156
|
-
|
157
|
-
|
158
|
-
<p><strong>Update:</strong> I’ve got an even faster method at the bottom now. Skip down there if you already know the basics of git-bisect.</p>
|
159
|
-
|
160
|
-
|
161
|
-
<p>Ok, now let’s move on to the cool shit, <a href="http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html">git-bisect</a>. Git-bisect helps you figure out exactly what code change broke a feature in your app, even when that code change was made months ago. It works by assisting you in a binary tree search through your commits, pausing at each one so that you can run a test and mark that commit as good or bad. This can remarkably decrease the amount of time you spend trying to figure out what is causing a new bug, because you quickly can find the exact code change that introduced it.</p>
|
162
|
-
|
163
|
-
|
164
|
-
<p>To use git-bisect, you first need a good test to run. Though you <em>could</em> do a manual test like loading a page in your browser and verifying that things look correct, you will be much happier if you write an <strong>automated test</strong> that you can run for each commit. Since I usually live in Ruby land, I’m fairly partial to <a href="http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">TestUnit</a> and <a href="http://rspec.info">rSpec</a> for my automated tests. If you’re in iPhone land, I strongly recommend using <a href="http://code.google.com/p/google-toolbox-for-mac/">google-toolbox-for-mac</a>.</p>
|
165
|
-
|
166
|
-
|
167
|
-
<p>With automated test in hand, you can kick off git-bisect with <code>git bisect start</code>. You then mark your current commit as bad using <code>git bisect bad</code>. You then checkout a known good commit using <code>git checkout commit_hash</code>. Run your test and mark it as good when it passes using <code>git bisect good</code>. At this point, git-bisect takes over and starts moving you through commit after commit. At each stop, you run your test and then mark the commit using either <code>git bisect bad</code> or <code>git bisect good</code>. At the end, git-bisect will tell you which commit first caused your error. You can then use <code>git diff commit_hash</code> to see what was changed in that commit. When you’re done, you run <code>git bisect reset</code> to set everything back to normal.</p>
|
168
|
-
|
169
|
-
|
170
|
-
<p>A typical git-bisect session looks somewhat like this:</p>
|
171
|
-
|
172
|
-
|
173
|
-
<pre>
|
174
|
-
(master) $ git bisect start
|
175
|
-
|
176
|
-
(master|BISECTING) $ git bisect bad
|
177
|
-
|
178
|
-
(master|BISECTING) $ git checkout eb5eecbb8fc4e2a964e8d2043d8b95f4eb7b563a
|
179
|
-
HEAD is now at eb5eecb... Add MainViewController
|
180
|
-
|
181
|
-
... run test which passes ...
|
182
|
-
|
183
|
-
(eb5eecb...|BISECTING) $ git bisect good
|
184
|
-
Bisecting: 3 revisions left to test after this
|
185
|
-
[d82c1595b6363484fe0d7f60f9ffa096d777bf17] First CompsView test
|
186
|
-
|
187
|
-
... run test which fails ...
|
188
|
-
|
189
|
-
(d82c159...|BISECTING) $ git bisect bad
|
190
|
-
Bisecting: 1 revisions left to test after this
|
191
|
-
[93af33167019fa039f5372dff602a76cbcbc99bb] Add first integration test
|
192
|
-
|
193
|
-
... run test which passes ...
|
194
|
-
|
195
|
-
(93af331...|BISECTING) $ git bisect good
|
196
|
-
Bisecting: 0 revisions left to test after this
|
197
|
-
[4f12091a287c363737ceb650df46196e5008d3f2] Add Comps target
|
198
|
-
|
199
|
-
... run test which fails ...
|
200
|
-
|
201
|
-
(4f12091...|BISECTING) $ git bisect bad
|
202
|
-
4f12091a287c363737ceb650df46196e5008d3f2 is first bad commit
|
203
|
-
commit 4f12091a287c363737ceb650df46196e5008d3f2
|
204
|
-
Author: Trotter Cashion &lt;cashion@example.com&gt;
|
205
|
-
Date: Tue Sep 23 20:18:09 2008 -0400
|
206
|
-
|
207
|
-
Add Comps target
|
208
|
-
|
209
|
-
:000000 100644 0000000000000000000000000000000000000000 789bf7877c6059a7f3ac8cb2b53fdb2c903e58ff A Comps-Info.plist
|
210
|
-
:040000 040000 d260571a48328d4a575a7395cd6ece3d651a93ac a622a23fdb80c915eaba49d1d53f7bf0dbf44a70 M ShootAndSpeak.xcodeproj
|
211
|
-
|
212
|
-
... Figure out what's wrong ...
|
213
|
-
|
214
|
-
(4f12091...|BISECTING) $ git bisect reset
|
215
|
-
Switched to branch "master"
|
216
|
-
|
217
|
-
</pre>
|
218
|
-
|
219
|
-
<p>I hope you learn to use and love git-bisect. It’s really helped me when trying to find the cause of nasty bugs that seemingly came out of nowhere.</p>
|
220
|
-
|
221
|
-
|
222
|
-
<h3>Update</h3>
|
223
|
-
|
224
|
-
|
225
|
-
<p>The above is too much work. While searching the <a href="http://desprofundis.blogspot.com/2008/06/git-bisect-instructions.html">net</a>, I found something even easier and faster. You can start git-bisect with the commit hashes like so <code>git bisect start bad_commit good_commit</code>. Even better, you can then tell git-bisect to run the tests itself… this is where things get awesome: <code>git bisect run some_test</code>. It’ll iterate through your commits until it finds the bad one. Checkout the sample session below.</p>
|
226
|
-
|
227
|
-
|
228
|
-
<pre>
|
229
|
-
/tmp/fake(master) $ git bisect start 68d5ab7a61a871fd097d8820e248cfd168395e4e 20cbc038973d6c78805bc8bfc3d187c2b537f183
|
230
|
-
Bisecting: 1 revisions left to test after this
|
231
|
-
[3da37ed0ee87c9129a61142ecefef17ab0de7f0f] Test works
|
232
|
-
|
233
|
-
/tmp/fake(3da37ed...|BISECTING) $ git bisect run testrb test/unit/some_test.rb -n test_truth
|
234
|
-
running testrb test/unit/some_test.rb -n test_truth
|
235
|
-
Loaded suite some_test.rb
|
236
|
-
Started
|
237
|
-
.
|
238
|
-
Finished in 0.000338 seconds.
|
239
|
-
|
240
|
-
1 tests, 1 assertions, 0 failures, 0 errors
|
241
|
-
Bisecting: 0 revisions left to test after this
|
242
|
-
[765d7e5c4eba730078907fc00121b8b35ada64b0] Test fails
|
243
|
-
running testrb test/unit/some_test.rb -n test_truth
|
244
|
-
Loaded suite some_test.rb
|
245
|
-
Started
|
246
|
-
F
|
247
|
-
Finished in 0.009607 seconds.
|
248
|
-
|
249
|
-
1) Failure:
|
250
|
-
test_truth(ThisTest) [./test/unit/some_test.rb:5]:
|
251
|
-
&lt;false&gt; is not true.
|
252
|
-
|
253
|
-
1 tests, 1 assertions, 1 failures, 0 errors
|
254
|
-
765d7e5c4eba730078907fc00121b8b35ada64b0 is first bad commit
|
255
|
-
commit 765d7e5c4eba730078907fc00121b8b35ada64b0
|
256
|
-
Author: Trotter Cashion &lt;cashion@example.com&gt;
|
257
|
-
Date: Sun Sep 28 13:13:09 2008 -0400
|
258
|
-
|
259
|
-
Test fails
|
260
|
-
|
261
|
-
:040000 040000 167dd04f2b4101ea256a7a6525859bc03e5433d3 0b062d4b5b03e7ac51ac4050fc7397c8983a2f13 M test
|
262
|
-
bisect run success
|
263
|
-
|
264
|
-
/tmp/fake(765d7e5...|BISECTING) $
|
265
|
-
|
266
|
-
</pre>
|
267
|
-
|
268
|
-
<p>As you can see above, I’m using ruby for this set of tests. My preferred method is to have git-bisect run testrb (or spec) and specify a single test for it to execute. This ensures that everything runs quite quickly.</p>
|
269
|
-
|
270
|
-
|
271
|
-
<h3>Land Here!</h3>
|
272
|
-
|
273
|
-
|
274
|
-
<p>If you jumped here from the top, I regret to inform you that this post will make you very sad. Turn back now before you’re stuck improving your life by <a href="http://git.or.cz/#download">installing git</a>. Once you’ve installed git, go check out <a href="http://peepcode.com/">Peepcode</a>. They’ve got a really good <a href="https://peepcode.com/products/git">screencast</a> and <a href="https://peepcode.com/products/git-internals-pdf">pdf</a> that explain git excellently.</p>
|
275
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/9/27/git-bisect-is-your-new-best-friend</feedburner:origLink></entry>
|
276
|
-
<entry xml:base="http://trottercashion.com/">
|
277
|
-
<author>
|
278
|
-
<name>trotter</name>
|
279
|
-
</author>
|
280
|
-
<id>tag:trottercashion.com,2008-08-08:31</id>
|
281
|
-
<published>2008-08-08T11:35:00Z</published>
|
282
|
-
<updated>2008-08-08T11:36:11Z</updated>
|
283
|
-
<category term="javascript" />
|
284
|
-
<category term="mocks" />
|
285
|
-
<category term="screw-unit" />
|
286
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/359351596/mocking-screw-unit" rel="alternate" type="text/html" />
|
287
|
-
<title>Mocking Screw-Unit</title>
|
288
|
-
<content type="html">
|
289
|
-
<p>I’m big on tests. Unit testing helps me clarify my thinking on problems and ensure that my code works well. When writing tests, it’s essential to have a good mocking framework to separate the things you are testing from the things you are not. In Ruby, I like using flexmock for Test::Unit and rSpec’s built in mocking framework when using it. In Javascript though, screw-unit doesn’t really come with a way to mock by default. (As an aside, screw-unit totally rocks for testing js.)</p>
|
290
|
-
|
291
|
-
|
292
|
-
<p>Thankfully, my coworker <a href="http://github.com/tobowers">Topper</a> (who’s a kickass dev, btw), has been playing around with adding mocking to screw-unit. He’s got a <a href="http://github.com/tobowers/screw-unit/tree/master">fork</a> on github, docs at the previous link, and a quick <a href="http://blog.toppingdesign.com/2008/07/22/screw-unit-javascript-mocking/">example blog post</a>. Click through and check this shit out, cause it’s hot.</p>
|
293
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/8/8/mocking-screw-unit</feedburner:origLink></entry>
|
294
|
-
<entry xml:base="http://trottercashion.com/">
|
295
|
-
<author>
|
296
|
-
<name>trotter</name>
|
297
|
-
</author>
|
298
|
-
<id>tag:trottercashion.com,2008-07-07:27</id>
|
299
|
-
<published>2008-07-07T17:19:00Z</published>
|
300
|
-
<updated>2008-07-07T17:21:30Z</updated>
|
301
|
-
<category term="floats" />
|
302
|
-
<category term="ruby" />
|
303
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/329041849/floating-pain" rel="alternate" type="text/html" />
|
304
|
-
<title>Floating Pain</title>
|
305
|
-
<content type="html">
|
306
|
-
<p><a href="http://blog.toppingdesign.com/">Topper</a> mentioned a tweet he saw to me in which someone asked why <code>(4.6 * 100).to_i #=&gt; 459</code>. Though this seems like a ruby bug, it’s really just one of the annoying things you hit with rounding errors and floats. At issue is that <code>#to_i</code> floors the float, instead of rounding it. Since the value may be approximated at 459.999999, the <code>#to_i</code> floors it to 459. To have things work like you’d expect, use <code>#round</code> when converting <code>Float</code> to <code>Fixnum</code>. See below for some code examples:</p>
|
307
|
-
|
308
|
-
|
309
|
-
<pre class="ruby">
|
310
|
-
4.6.to_i # =&gt; 4
|
311
|
-
4.6.round # =&gt; 5
|
312
|
-
(4.6 * 100).to_i # =&gt; 459
|
313
|
-
(4.6 * 100).round # =&gt; 460
|
314
|
-
</pre>
|
315
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/7/7/floating-pain</feedburner:origLink></entry>
|
316
|
-
<entry xml:base="http://trottercashion.com/">
|
317
|
-
<author>
|
318
|
-
<name>trotter</name>
|
319
|
-
</author>
|
320
|
-
<id>tag:trottercashion.com,2008-06-27:26</id>
|
321
|
-
<published>2008-06-27T03:06:00Z</published>
|
322
|
-
<updated>2008-06-27T03:07:05Z</updated>
|
323
|
-
<category term="programming" />
|
324
|
-
<category term="trotter cashion" />
|
325
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/321023468/how-i-got-started-programming" rel="alternate" type="text/html" />
|
326
|
-
<title>How I Got Started Programming</title>
|
327
|
-
<content type="html">
|
328
|
-
<p><a href="http://www.pauldix.net/2008/06/how-i-got-start.html">Paul</a> says I’ve got to do this, and I don’t want to let him
|
329
|
-
down. <a href="http://gilesbowkett.blogspot.com/2008/06/how-i-got-started-programming.html">Giles</a> tagged him first, so you should probably read his too.</p>
|
330
|
-
|
331
|
-
|
332
|
-
<h3>How old were you when you started programming?</h3>
|
333
|
-
|
334
|
-
|
335
|
-
<p>In third grade (when I was 8) I started started taking super nerd math classes
|
336
|
-
with other super nerds. As part of those classes, they had us programming a
|
337
|
-
turtle to draw things on the screen. <a href="http://en.wikipedia.org/wiki/Logo_(programming_language">Logo</a>) was totally awesome and had me hooked
|
338
|
-
on the magic of programming.</p>
|
339
|
-
|
340
|
-
|
341
|
-
<h3>How did you get started programming?</h3>
|
342
|
-
|
343
|
-
|
344
|
-
<p>After Logo, my dad (he was a <span class="caps">CTO</span> at the time) bought me Visual Studio and a few
|
345
|
-
books on Visual Basic. It was lots of “Teach Yourself X in X days”, and I ran
|
346
|
-
through VB, C++, and a little Delphi. Naturally, those books didn’t teach me to
|
347
|
-
actually be good, though I did figure out how to make a few small games that I
|
348
|
-
could play.</p>
|
349
|
-
|
350
|
-
|
351
|
-
<p>My dad was a <span class="caps">CTO</span> at an investment bank, which is the kind of place that treats a
|
352
|
-
<span class="caps">CTO</span> like crap. I didn’t want to be the guy that got shat on, so in high school I
|
353
|
-
dropped programming and started learning businessy things. I even picked my
|
354
|
-
college based on the strength of its business school. Once I showed up, I
|
355
|
-
realized that I didn’t like anyone at the business school, that philosophy was
|
356
|
-
fun, and that math/econ could make me money. I promptly switched my major.</p>
|
357
|
-
|
358
|
-
|
359
|
-
<p>After college, I went to work at a job that I ended up hating, quit it, took a
|
360
|
-
few months to figure out my life, and realized that I really loved programming.
|
361
|
-
Thankfully I got lucky and read a <a href="http://lifehacker.com/software/notag/ruby-on-rails-book-as-a-download-105594.php">blog post</a> that tipped me to
|
362
|
-
the beta book of <span class="caps">AWDWR</span>, which taught me a lot about <strong>real</strong> programming. I
|
363
|
-
consider that the start of me becomming a real programmer, and not just some kid
|
364
|
-
that can code.</p>
|
365
|
-
|
366
|
-
|
367
|
-
<h3>What was your first language?</h3>
|
368
|
-
|
369
|
-
|
370
|
-
<p>Logo! Drawing with turtles rocks so hard. After that it was VB, which let me
|
371
|
-
push Windows around and made me a little cash.</p>
|
372
|
-
|
373
|
-
|
374
|
-
<h3>What was the first real program you wrote?</h3>
|
375
|
-
|
376
|
-
|
377
|
-
<p>I wrote my first useful program while working as an intern at a financial
|
378
|
-
services firm. The company was using <a href="http://www.advent.com/solutions/asset_managers/axys_platform">Axys</a> (really bad website alert!) for portfolio analysis and had a
|
379
|
-
tedious process for reconciling their branches with the back office. I wrote a
|
380
|
-
VB program that helped them to perform these reconciliations more quickly, which
|
381
|
-
I hear they still use.</p>
|
382
|
-
|
383
|
-
|
384
|
-
<h3>What languages have you used since you started programming?</h3>
|
385
|
-
|
386
|
-
|
387
|
-
<p>Logo, VB, C, C++, Delphi, Ruby, Objective-C, Erlang, Scheme, Javascript, Java, and maybe something else. Of those, I’d feel comfortable working on a project using Ruby, Javascript, Objective-C, or Erlang. I’m skilled enough in some of the others, but have vowed to never use them again. I’ll let you guess which.</p>
|
388
|
-
|
389
|
-
|
390
|
-
<h3>What was your first professional programming gig?</h3>
|
391
|
-
|
392
|
-
|
393
|
-
<p>2005 at the Nathan Kline Institute for Mental Health. There was a PhD there who
|
394
|
-
needed <a href="http://en.wikipedia.org/wiki/ImageJ">ImageJ</a> to talk to his microscope over a serial port and to have a
|
395
|
-
lot of old scripts from ObjectImage translated into ImageJ. It was a fun job
|
396
|
-
that let me work at my own pace and play a lot with the art of programming.</p>
|
397
|
-
|
398
|
-
|
399
|
-
<h3>If there is one thing you learned along the way that you would tell new
|
400
|
-
developers, what would it be?</h3>
|
401
|
-
|
402
|
-
|
403
|
-
<p>Surround yourself with great people, and never be the smartest guy in the room. If you’re lucky enough to work at a
|
404
|
-
company with some great programmers, you’ll learn a whole lot that way. If
|
405
|
-
your company is full of 9-5 coders, join a local developer group or start your
|
406
|
-
own. <a href="http://nycruby.org">Nyc.rb</a> and <a href="http://www.phillyonrails.org/">Philly On Rails</a> totally rock, so you could always move to New York or Philly and learn from some of the best.</p>
|
407
|
-
|
408
|
-
|
409
|
-
<h3>What’s the most fun you’ve ever had programming?</h3>
|
410
|
-
|
411
|
-
|
412
|
-
<p>Logo. I used to love making that little turtle draw all sorts of fun things on
|
413
|
-
the screen. There was no real need to make the turtle do things, I was doing it
|
414
|
-
just for the joy of it. I managed to recreate some of that feeling when working
|
415
|
-
on <a href="http://rubyforge.org/projects/spec-unit/">spec-unit</a>, which is really my only useful open source contribution to
|
416
|
-
date. Unfortunately, it only has one release ever, and I haven’t messed with it in two years.</p>
|
417
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/6/27/how-i-got-started-programming</feedburner:origLink></entry>
|
418
|
-
<entry xml:base="http://trottercashion.com/">
|
419
|
-
<author>
|
420
|
-
<name>trotter</name>
|
421
|
-
</author>
|
422
|
-
<id>tag:trottercashion.com,2008-06-22:5</id>
|
423
|
-
<published>2008-06-22T22:23:00Z</published>
|
424
|
-
<updated>2008-06-22T22:44:05Z</updated>
|
425
|
-
<category term="business" />
|
426
|
-
<category term="working remotely" />
|
427
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/317690654/on-working-remotely" rel="alternate" type="text/html" />
|
428
|
-
<title>On Working Remotely</title>
|
429
|
-
<content type="html">
|
430
|
-
<p>Being on my honeymoon in St. Barths has got me thinking a bit about working remotely. Don’t worry, I’m not working on this trip. I’m more trying to think if ways I could stay on this trip indefinitely, but still manage to make some cash.</p>
|
431
|
-
|
432
|
-
|
433
|
-
<p>I’m no stranger to remote work. As some of you may know, I live in Philly but continue to work for <a href="http://www.motionbox.com">Motionbox</a> in New York. I commute two days per week, and spend the rest working from home in Philly. Over the past year, I’ve started to catalog my likes and dislikes with this arrangement, and I’m going to list some of my dislikes here along with some possible ways to improve things. For now, we’ll just look at the first problem I’ve encountered:</p>
|
434
|
-
|
435
|
-
|
436
|
-
<h3>You’re out of touch</h3>
|
437
|
-
|
438
|
-
|
439
|
-
<p>If you’re distributed while the rest of the team is collocated, you <strong>will</strong> be out of the loop. When your boss is walking around the office and stopping at various desks, he won’t be stopping at yours. If you’re looking to be recognized for your accomplishments, this can be a major problem. It’s difficult to advance in the company if you’re not visible.</p>
|
440
|
-
|
441
|
-
|
442
|
-
<p>To combat this problem, I’ve found getting everyone on IM and <span class="caps">IRC</span> to be very helpful. If your company uses an open office, you’re in luck. The noise from the floor plan typically causes people to use headphones, so they’ll be much more prone to use IM and <span class="caps">IRC</span> for all their communication (even with those people right next to them). Another good technique is to send copious amounts of email. If people are cataloging what should be done and who has done what through email (a good practice regardless), then it’s much easier for you to keep track of what is happening in the office.</p>
|
443
|
-
|
444
|
-
|
445
|
-
<p>I don’t think that Skype or frequent phone calls help much in this regard. Typically, you’re only talking to one person and all the speaker phone arrangements I’ve seen aren’t that great. Voice is great for quickly hashing out the details of a plan with one other person, but is terrible as a mechanism for keeping up with the goings on of the company.</p>
|
446
|
-
|
447
|
-
|
448
|
-
<p>Making time (and spending the money) to get to the office at least once a month is invaluable. Though email, IM, and <span class="caps">IRC</span> help, they’re not a real substitute for quality time in person. One of the most important things I’ve done at Motionbox is to know when people are going out for after work drinks to celebrate various accomplishments and made sure that I was able to be in town for them. Though it sounds silly to talk about drinking as an important part of work, the main thing you miss by being remote is the social component. It’s much more important to get in town to socialize than it is to do actual work. You’ll have plenty of time to work when you’re home alone the next day.</p>
|
449
|
-
|
450
|
-
|
451
|
-
<p>Anyone have any thoughts on other ways to keep in touch while working remotely?</p>
|
452
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/6/22/on-working-remotely</feedburner:origLink></entry>
|
453
|
-
<entry xml:base="http://trottercashion.com/">
|
454
|
-
<author>
|
455
|
-
<name>trotter</name>
|
456
|
-
</author>
|
457
|
-
<id>tag:trottercashion.com,2008-06-11:20</id>
|
458
|
-
<published>2008-06-11T02:30:00Z</published>
|
459
|
-
<updated>2008-06-11T02:46:16Z</updated>
|
460
|
-
<category term="belongstodemeter" />
|
461
|
-
<category term="rails" />
|
462
|
-
<category term="ruby" />
|
463
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/309293334/belongstodemeter" rel="alternate" type="text/html" />
|
464
|
-
<title>BelongsToDemeter</title>
|
465
|
-
<content type="html">
|
466
|
-
<p>While playing with Rails the other day, I thought it would be fun if you could get at attributes of a belongs_to association without having to do the whole traverse association and check for nil thing.</p>
|
467
|
-
|
468
|
-
<pre class="ruby">
|
469
|
-
|
470
|
-
# Something like...
|
471
|
-
@person.group_name # => "Pizza Fans!" || nil
|
472
|
-
|
473
|
-
# Instead of...
|
474
|
-
@person.group ? @person.group.name : nil # => "Pizza Fans!" || nil
|
475
|
-
|
476
|
-
</pre>
|
477
|
-
|
478
|
-
<p>Thinking this would be a fun chance to play with some meta, I threw together <a href="http://github.com/trotter/belongs_to_demeter/tree/master">BelongsToDemeter</a>, which you can find over on <a href="http://github.com">GitHub</a>. It's a rails plugin, but don't expect it to actually install using script/plugin. The code is complete and utter crap, so it's probably best that Rails won't install it. It is slow, and most likely prone to error. Still, it's a fun little thought experiment, and I may decide to clean it up then speed it up if someone tells me they like it.</p>
|
479
|
-
|
480
|
-
<p>It does what I explained above and also lets you do fun things like this, which I think are useful when assigning associations through a form:</p>
|
481
|
-
|
482
|
-
<pre class="ruby">
|
483
|
-
|
484
|
-
# Lookup the user 'Bob' by login and assign
|
485
|
-
# it to the user association
|
486
|
-
@character.user # => nil
|
487
|
-
@character.user_login = "bob"
|
488
|
-
@character.user.login # => "bob"
|
489
|
-
|
490
|
-
</pre>
|
491
|
-
|
492
|
-
<p>Anyway, go over to <a href="http://github.com">GitHub</a> and check out <a href="http://github.com/trotter/belongs_to_demeter/tree/master">BelongsToDemeter</a>. When you're done, let me know if you like the concept. After all that, go erase all memory of the implementation details from your mind, they're ugly.</p>
|
493
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/6/11/belongstodemeter</feedburner:origLink></entry>
|
494
|
-
<entry xml:base="http://trottercashion.com/">
|
495
|
-
<author>
|
496
|
-
<name>trotter</name>
|
497
|
-
</author>
|
498
|
-
<id>tag:trottercashion.com,2008-06-05:16</id>
|
499
|
-
<published>2008-06-05T02:22:00Z</published>
|
500
|
-
<updated>2008-06-11T02:48:00Z</updated>
|
501
|
-
<category term="macro tests" />
|
502
|
-
<category term="rspec" />
|
503
|
-
<category term="ruby" />
|
504
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/304990208/macro-tests" rel="alternate" type="text/html" />
|
505
|
-
<title>Macro Tests</title>
|
506
|
-
<content type="html">
|
507
|
-
<p>I went to <a href="http://blog.hasmanythrough.com/">Josh Susser</a>'s talk at RailsConf last week, where he mentioned that no one really writes macro tests with rSpec. Naturally, I found this to be a true shame, since I think macro tests are a great way to clean up your code. What's a macro test you ask? Sit right there and I'll tell you.</p>
|
508
|
-
|
509
|
-
<p>A macro test is a test that defines other tests for you. It's a great way to reduce repetition in your test code, thereby making it easier to read. Take these three tests as an example:<p>
|
510
|
-
|
511
|
-
<pre class="ruby">
|
512
|
-
describe 'GET /users/:user_id/posts/:post_id/comments' do
|
513
|
-
it 'should be a success' do
|
514
|
-
get :index, :user_id =&gt; 2, :post_id =&gt; 3
|
515
|
-
response.should be_success
|
516
|
-
end
|
517
|
-
|
518
|
-
it 'should 404 without a user id' do
|
519
|
-
get :index, :post_id =&gt; 3
|
520
|
-
response.headers['Status'].to_i.should == 404
|
521
|
-
end
|
522
|
-
|
523
|
-
it 'should 404 without a post id' do
|
524
|
-
get :index, :user_id =&gt; 2
|
525
|
-
response.headers['Status'].to_i.should == 404
|
526
|
-
end
|
527
|
-
end
|
528
|
-
</pre>
|
529
|
-
|
530
|
-
<p>Obviously there's a little bit of duplication in the 404's. Let's define macro tests to clean that up.</p>
|
531
|
-
|
532
|
-
<pre class="ruby">
|
533
|
-
describe 'GET /users/:user_id/posts/:post_id/comments' do
|
534
|
-
def self.should_404_without(param)
|
535
|
-
# Since you're calling this method within the describe block,
|
536
|
-
# it is being called within the correct context.
|
537
|
-
it "should 404 without #{param}" do
|
538
|
-
get :index, paramz.merge(param.to_sym =&gt; nil)
|
539
|
-
response.headers['Status'].to_i.should == 404
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
should_404_without "user_id"
|
544
|
-
should_404_without "post_id"
|
545
|
-
|
546
|
-
# Normally I put this at the bottom, only here to keep it close to the above.
|
547
|
-
# This method is called by the test the macro test defines to hand off the params.
|
548
|
-
def paramz
|
549
|
-
{:user_id =&gt; 2, :post_id =&gt; 3}
|
550
|
-
end
|
551
|
-
|
552
|
-
it 'should be a success' do
|
553
|
-
get :index, paramz
|
554
|
-
response.should be_success
|
555
|
-
end
|
556
|
-
end
|
557
|
-
</pre>
|
558
|
-
|
559
|
-
<p>Hmm... this doesn't really look any cleaner. The problem is that we did this for only one action. If we generalize should_404_without more, then we can put it into its own shared example set that we can include in any describe block. Let's look at that now.</p>
|
560
|
-
|
561
|
-
<pre class="ruby">
|
562
|
-
shared_examples_for "controllers" do
|
563
|
-
def self.should_normally_succeed
|
564
|
-
it 'should be a success' do
|
565
|
-
get @action, paramz
|
566
|
-
response.should be_success
|
567
|
-
end
|
568
|
-
end
|
569
|
-
|
570
|
-
def self.should_404_without(param)
|
571
|
-
it "should 404 without #{param}" do
|
572
|
-
get @action, paramz.merge(param.to_sym =&gt; nil)
|
573
|
-
response.headers['Status'].to_i.should == 404
|
574
|
-
end
|
575
|
-
end
|
576
|
-
end
|
577
|
-
|
578
|
-
describe 'GET /users/:user_id/posts/:post_id/comments' do
|
579
|
-
it_should_behave_like "controllers"
|
580
|
-
|
581
|
-
before(:each) { @action = :index }
|
582
|
-
|
583
|
-
should_normally_succeed
|
584
|
-
should_404_without "user_id"
|
585
|
-
should_404_without "post_id"
|
586
|
-
|
587
|
-
def paramz
|
588
|
-
{:user_id =&gt; 2, :post_id =&gt; 3}
|
589
|
-
end
|
590
|
-
end
|
591
|
-
</pre>
|
592
|
-
|
593
|
-
<p>Oh snap! That describe block is a lot cleaner, and we can tuck the shared examples into spec_helper.rb to really clean things up. Using macro tests, you'll find it's very easy to create a lot of REST controllers very quickly. In fact, I plan on open sourcing something to help with that very soon...</p>
|
594
|
-
|
595
|
-
<p><strong>Update</strong>: See the comments for David's advice on pulling this out of shared examples and into a module that you can then add to rSpec. This cleans up the describe blocks even more. Mega win!</p>
|
596
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/6/5/macro-tests</feedburner:origLink></entry>
|
597
|
-
<entry xml:base="http://trottercashion.com/">
|
598
|
-
<author>
|
599
|
-
<name>trotter</name>
|
600
|
-
</author>
|
601
|
-
<id>tag:trottercashion.com,2008-05-04:12</id>
|
602
|
-
<published>2008-05-04T04:14:00Z</published>
|
603
|
-
<updated>2008-05-04T14:39:31Z</updated>
|
604
|
-
<category term="moving" />
|
605
|
-
<category term="starting" />
|
606
|
-
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/283393385/starting-the-car" rel="alternate" type="text/html" />
|
607
|
-
<title>Starting the Car</title>
|
608
|
-
<content type="html">
|
609
|
-
<p>Well, it's not much of a start, but every trip must have a beginning. As I wake up and wipe my bleary eyes, I'm going to slowly pull this car out of the driveway and put it on the road. The tunes aren't yet cranking; no Allman Brothers is yet helping us greet the open road. Still, we've left that old home that is <a href="http://lifecoding.com">lifecoding</a> and are starting out on something new, something fresh. We're running on rubies with a comfortable walking shoe pushing the accelerator. I hope you enjoy this ride.</p>
|
610
|
-
</content> <feedburner:origLink>http://trottercashion.com/2008/5/4/starting-the-car</feedburner:origLink></entry>
|
611
|
-
</feed>
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
3
|
+
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-US">
|
4
|
+
<title>TrotterCashion - Home</title>
|
5
|
+
<id>tag:trottercashion.com,2008:mephisto/</id>
|
6
|
+
<generator uri="http://mephistoblog.com" version="0.8.0">Mephisto Drax</generator>
|
7
|
+
|
8
|
+
<link href="http://trottercashion.com/" rel="alternate" type="text/html" />
|
9
|
+
<updated>2008-12-13T17:48:58Z</updated>
|
10
|
+
<link rel="self" href="http://feeds.feedburner.com/trottercashion" type="application/atom+xml" /><entry xml:base="http://trottercashion.com/">
|
11
|
+
<author>
|
12
|
+
<name>trotter</name>
|
13
|
+
</author>
|
14
|
+
<id>tag:trottercashion.com,2008-12-13:53</id>
|
15
|
+
<published>2008-12-13T17:43:00Z</published>
|
16
|
+
<updated>2008-12-13T17:48:58Z</updated>
|
17
|
+
<category term="dataurls" />
|
18
|
+
<category term="javascript" />
|
19
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/483840026/data-urls-and-document-domain" rel="alternate" type="text/html" />
|
20
|
+
<title>Data Urls and document.domain</title>
|
21
|
+
<content type="html">
|
22
|
+
<p>Well this is a bummer. It turns out that all data urls share a common domain of ””. This is a problem in <span class="caps">HTML5</span>, because access to sqlite databases is based on the document.domain (This is true in safari at least). Therefore, all data urls will share a common sqlite db environment, meaning that a data url from Google,could look in the database created by a Yahoo data url, given that they were able to guess the name of the database. Since I see data urls as a better way to do offline web apps than google gears, this is a problem that pains me. Does anyone know if there is a solution?</p>
|
23
|
+
|
24
|
+
|
25
|
+
<p>My main thought on how to fix this would be to require the domain for any data url that is a target of an link be set to the domain of the linker. The same would go for any data url that is loaded via a src=””, but this shouldn’t matter as all scripts use the document domain and not their own domain for security purposes. In cases where this is no linker, data urls get their domain set to an md5 hash of their data. Anyone see any problems with this solution?</p>
|
26
|
+
|
27
|
+
|
28
|
+
<p>If you don’t know what data urls are, check out my <a href="http://trottercashion.com/2008/12/12/data-urls-are-fun">previous post</a>.</p>
|
29
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/12/13/data-urls-and-document-domain</feedburner:origLink></entry>
|
30
|
+
<entry xml:base="http://trottercashion.com/">
|
31
|
+
<author>
|
32
|
+
<name>trotter</name>
|
33
|
+
</author>
|
34
|
+
<id>tag:trottercashion.com,2008-12-12:48</id>
|
35
|
+
<published>2008-12-12T13:03:00Z</published>
|
36
|
+
<updated>2008-12-12T13:04:13Z</updated>
|
37
|
+
<category term="dataurls" />
|
38
|
+
<category term="iphone" />
|
39
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/482664882/data-urls-are-fun" rel="alternate" type="text/html" />
|
40
|
+
<title>Data Urls Are Fun!</title>
|
41
|
+
<content type="html">
|
42
|
+
<p>Lately I’ve been playing with data urls in an effort to use them as an alternative way to build iPhone apps. W. Clawpaws wrote an interesting <a href="http://blog.clawpaws.net/post/2007/07/16/Storing-iPhone-apps-locally-with-data-URLs">post</a> on this a year ago, but it seems that not much has been done since. If it’s possible, I plan to have a simple library built by end of year that will allow you to write data url apps that connect to a central server when available. Basically, it’ll make the persistence and speed arguments for writing native apps null and void.</p>
|
43
|
+
|
44
|
+
|
45
|
+
<p>Anyway, enough of what I plan to do and more about actual data urls. “Data urls”http://en.wikipedia.org/wiki/Data_URI_scheme allow you to store a single image, some javascript, or even an entire web page in a url. The browser will then render that information as if it were pulling it from a normal http:// url. So they will increase the initial payload of your web page, but result in faster interactions once the page is loaded.</p>
|
46
|
+
|
47
|
+
|
48
|
+
<p>Data urls have a format of</p>
|
49
|
+
|
50
|
+
|
51
|
+
<pre>data:[&lt;MIME-type&gt;][;charset="&lt;encoding&gt;"][;base64],&lt;data&gt;</pre>
|
52
|
+
|
53
|
+
<p>So if you see something like data:text/html;base64,PGh0bWw+, you’re looking at a data url. To see one in action, click the link below and have a look at the url in your navigation bar.</p>
|
54
|
+
|
55
|
+
|
56
|
+
<p>
|
57
|
+
<a>Data url in action!</a>
|
58
|
+
</p>
|
59
|
+
|
60
|
+
<p>Check back in the near future for progress on the iPhone idea. In the meantime, enjoy playing with data urls.</p>
|
61
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/12/12/data-urls-are-fun</feedburner:origLink></entry>
|
62
|
+
<entry xml:base="http://trottercashion.com/">
|
63
|
+
<author>
|
64
|
+
<name>trotter</name>
|
65
|
+
</author>
|
66
|
+
<id>tag:trottercashion.com,2008-12-11:44</id>
|
67
|
+
<published>2008-12-11T14:48:00Z</published>
|
68
|
+
<updated>2008-12-11T14:49:22Z</updated>
|
69
|
+
<category term="iphone" />
|
70
|
+
<category term="rails" />
|
71
|
+
<category term="speaking" />
|
72
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/481739351/iphone-on-rails-the-presentation" rel="alternate" type="text/html" />
|
73
|
+
<title>iPhone on Rails: The Presentation</title>
|
74
|
+
<content type="html">
|
75
|
+
<p>Last night’s presentation went pretty well. I gave a fairly high level (except for the XCode part) overview of developing web and native apps for the iPhone. I hope to spruce this one up based on the questions asked during the talk last night and give it again some place else. Anyway, slides are below.</p>
|
76
|
+
|
77
|
+
|
78
|
+
<div><a href="http://www.slideshare.net/trotter/iphonerails-presentation?type=powerpoint" title="iPhone on Rails">iPhone on Rails</a>&lt;object height="355" width="425">&lt;param />&lt;param />&lt;param />&lt;embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=iphonerails-1229006573528763-1&amp;#38;stripped_title=iphonerails-presentation" height="355" width="425">&lt;/embed>&lt;/object><div>View SlideShare <a href="http://www.slideshare.net/trotter/iphonerails-presentation?type=powerpoint" title="View iPhone on Rails on SlideShare">presentation</a> or <a href="http://www.slideshare.net/upload?type=powerpoint">Upload</a> your own. (tags: <a href="http://slideshare.net/tag/rails">rails</a> <a href="http://slideshare.net/tag/iphone">iphone</a>)</div></div>
|
79
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/12/11/iphone-on-rails-the-presentation</feedburner:origLink></entry>
|
80
|
+
<entry xml:base="http://trottercashion.com/">
|
81
|
+
<author>
|
82
|
+
<name>trotter</name>
|
83
|
+
</author>
|
84
|
+
<id>tag:trottercashion.com,2008-12-10:43</id>
|
85
|
+
<published>2008-12-10T16:17:00Z</published>
|
86
|
+
<updated>2008-12-10T16:17:37Z</updated>
|
87
|
+
<category term="iphone" />
|
88
|
+
<category term="speaking" />
|
89
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/480717107/iphone-on-rails" rel="alternate" type="text/html" />
|
90
|
+
<title>iPhone on Rails</title>
|
91
|
+
<content type="html">
|
92
|
+
<p>I’m a little late to this party, but I’m speaking at <a href="http://www.phillyonrails.org/events/3">philly.rb</a> tonight. The talk is about making rails backed iPhone apps, both web and native. It should be a raucous good time, so come on over if you’re near Philly.</p>
|
93
|
+
|
94
|
+
|
95
|
+
<p>As an aside, there’s a chance that this will be taped or that I’ll actually upload my slides for once. That said, if you miss the talk, you’re probably shit out of luck for my valuable knowledge.</p>
|
96
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/12/10/iphone-on-rails</feedburner:origLink></entry>
|
97
|
+
<entry xml:base="http://trottercashion.com/">
|
98
|
+
<author>
|
99
|
+
<name>trotter</name>
|
100
|
+
</author>
|
101
|
+
<id>tag:trottercashion.com,2008-10-01:39</id>
|
102
|
+
<published>2008-10-01T23:17:00Z</published>
|
103
|
+
<updated>2008-10-01T23:39:57Z</updated>
|
104
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/408719167/iphone-resources" rel="alternate" type="text/html" />
|
105
|
+
<title>iPhone Resources</title>
|
106
|
+
<content type="html">
|
107
|
+
<p>I’m sure you’ve heard by now that the <a href="http://www.tuaw.com/2008/10/01/iphone-nda-dropped/">iPhone <span class="caps">NDA</span> has been lifted</a>. This is great news for those of us that pretend to be iPhone developers, because we’re going to start seeing a lot more resources at our fingertips.</p>
|
108
|
+
|
109
|
+
|
110
|
+
<p>I’m going to start keeping a moderated page of <a href="http://trottercashion.com/iphone-development">iPhone development resources</a> including links to blog posts, books, and maybe even podcasts. If you see any blog posts (I’m sure there’ll be tons in the next few days). Let me know and I’ll add them to the list.</p>
|
111
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/10/1/iphone-resources</feedburner:origLink></entry>
|
112
|
+
<entry xml:base="http://trottercashion.com/">
|
113
|
+
<author>
|
114
|
+
<name>trotter</name>
|
115
|
+
</author>
|
116
|
+
<id>tag:trottercashion.com,2008-10-01:38</id>
|
117
|
+
<published>2008-10-01T23:10:00Z</published>
|
118
|
+
<updated>2008-10-01T23:11:49Z</updated>
|
119
|
+
<category term="javascript" />
|
120
|
+
<category term="rspec" />
|
121
|
+
<category term="screw-unit" />
|
122
|
+
<category term="testing" />
|
123
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/408699804/mocking-screw-unit-part-deux" rel="alternate" type="text/html" />
|
124
|
+
<title>Mocking Screw-Unit Part Deux</title>
|
125
|
+
<content type="html">
|
126
|
+
<p>I <a href="http://trottercashion.com/2008/8/8/mocking-screw-unit">wrote earlier</a> about how <a href="http://github.com/tobowers">Topper</a> <a href="http://blog.toppingdesign.com/2008/07/22/screw-unit-javascript-mocking/">mocked out the dom</a> for screw-unit testing. Taking his lead, I started playing with screw-unit and adding some mocking and stubbing in the <a href="http://rspec.info">rspec</a> way. It’s not quite release worthy, but it’s on github now and I think it’s nearly usable. Basically, it lets you do things like this:</p>
|
127
|
+
|
128
|
+
|
129
|
+
<pre class="javascript">
|
130
|
+
user = {login: 'bob'};
|
131
|
+
Screw.Stub.stub(user, 'login').andReturn('nancy');
|
132
|
+
user.login; // =&gt; 'nancy'
|
133
|
+
Screw.Stub.reset(); // Called automatically after each spec
|
134
|
+
user.login; // =&gt; 'bob'
|
135
|
+
|
136
|
+
// Will throw a spec failure if user.email() is never called.
|
137
|
+
Screw.stub.shouldReceive(user, 'email');
|
138
|
+
</pre>
|
139
|
+
|
140
|
+
<p>Obviously shouldReceive is not quite complete. It’s missing with(), numberOfTimes(), and other things. Still, it’s good enough that others can start iterating on the model I’ve laid down. As I said earlier, my <a href="http://github.com/trotter/screw-unit/tree/master">fork of screw-unit</a> is available on github now, so have a look and feel free to leave questions in the comments.</p>
|
141
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/10/1/mocking-screw-unit-part-deux</feedburner:origLink></entry>
|
142
|
+
<entry xml:base="http://trottercashion.com/">
|
143
|
+
<author>
|
144
|
+
<name>trotter</name>
|
145
|
+
</author>
|
146
|
+
<id>tag:trottercashion.com,2008-09-27:33</id>
|
147
|
+
<published>2008-09-27T23:33:00Z</published>
|
148
|
+
<updated>2008-10-14T14:10:17Z</updated>
|
149
|
+
<category term="bugs" />
|
150
|
+
<category term="git" />
|
151
|
+
<category term="git-bisect" />
|
152
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/405022957/git-bisect-is-your-new-best-friend" rel="alternate" type="text/html" />
|
153
|
+
<title>git-bisect Is Your New Best Friend</title>
|
154
|
+
<content type="html">
|
155
|
+
<p>To anyone not using <a href="http://en.wikipedia.org/wiki/Git_(software)">git</a>, jump to the bottom of the post then come back up.</p>
|
156
|
+
|
157
|
+
|
158
|
+
<p><strong>Update:</strong> I’ve got an even faster method at the bottom now. Skip down there if you already know the basics of git-bisect.</p>
|
159
|
+
|
160
|
+
|
161
|
+
<p>Ok, now let’s move on to the cool shit, <a href="http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html">git-bisect</a>. Git-bisect helps you figure out exactly what code change broke a feature in your app, even when that code change was made months ago. It works by assisting you in a binary tree search through your commits, pausing at each one so that you can run a test and mark that commit as good or bad. This can remarkably decrease the amount of time you spend trying to figure out what is causing a new bug, because you quickly can find the exact code change that introduced it.</p>
|
162
|
+
|
163
|
+
|
164
|
+
<p>To use git-bisect, you first need a good test to run. Though you <em>could</em> do a manual test like loading a page in your browser and verifying that things look correct, you will be much happier if you write an <strong>automated test</strong> that you can run for each commit. Since I usually live in Ruby land, I’m fairly partial to <a href="http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">TestUnit</a> and <a href="http://rspec.info">rSpec</a> for my automated tests. If you’re in iPhone land, I strongly recommend using <a href="http://code.google.com/p/google-toolbox-for-mac/">google-toolbox-for-mac</a>.</p>
|
165
|
+
|
166
|
+
|
167
|
+
<p>With automated test in hand, you can kick off git-bisect with <code>git bisect start</code>. You then mark your current commit as bad using <code>git bisect bad</code>. You then checkout a known good commit using <code>git checkout commit_hash</code>. Run your test and mark it as good when it passes using <code>git bisect good</code>. At this point, git-bisect takes over and starts moving you through commit after commit. At each stop, you run your test and then mark the commit using either <code>git bisect bad</code> or <code>git bisect good</code>. At the end, git-bisect will tell you which commit first caused your error. You can then use <code>git diff commit_hash</code> to see what was changed in that commit. When you’re done, you run <code>git bisect reset</code> to set everything back to normal.</p>
|
168
|
+
|
169
|
+
|
170
|
+
<p>A typical git-bisect session looks somewhat like this:</p>
|
171
|
+
|
172
|
+
|
173
|
+
<pre>
|
174
|
+
(master) $ git bisect start
|
175
|
+
|
176
|
+
(master|BISECTING) $ git bisect bad
|
177
|
+
|
178
|
+
(master|BISECTING) $ git checkout eb5eecbb8fc4e2a964e8d2043d8b95f4eb7b563a
|
179
|
+
HEAD is now at eb5eecb... Add MainViewController
|
180
|
+
|
181
|
+
... run test which passes ...
|
182
|
+
|
183
|
+
(eb5eecb...|BISECTING) $ git bisect good
|
184
|
+
Bisecting: 3 revisions left to test after this
|
185
|
+
[d82c1595b6363484fe0d7f60f9ffa096d777bf17] First CompsView test
|
186
|
+
|
187
|
+
... run test which fails ...
|
188
|
+
|
189
|
+
(d82c159...|BISECTING) $ git bisect bad
|
190
|
+
Bisecting: 1 revisions left to test after this
|
191
|
+
[93af33167019fa039f5372dff602a76cbcbc99bb] Add first integration test
|
192
|
+
|
193
|
+
... run test which passes ...
|
194
|
+
|
195
|
+
(93af331...|BISECTING) $ git bisect good
|
196
|
+
Bisecting: 0 revisions left to test after this
|
197
|
+
[4f12091a287c363737ceb650df46196e5008d3f2] Add Comps target
|
198
|
+
|
199
|
+
... run test which fails ...
|
200
|
+
|
201
|
+
(4f12091...|BISECTING) $ git bisect bad
|
202
|
+
4f12091a287c363737ceb650df46196e5008d3f2 is first bad commit
|
203
|
+
commit 4f12091a287c363737ceb650df46196e5008d3f2
|
204
|
+
Author: Trotter Cashion &lt;cashion@example.com&gt;
|
205
|
+
Date: Tue Sep 23 20:18:09 2008 -0400
|
206
|
+
|
207
|
+
Add Comps target
|
208
|
+
|
209
|
+
:000000 100644 0000000000000000000000000000000000000000 789bf7877c6059a7f3ac8cb2b53fdb2c903e58ff A Comps-Info.plist
|
210
|
+
:040000 040000 d260571a48328d4a575a7395cd6ece3d651a93ac a622a23fdb80c915eaba49d1d53f7bf0dbf44a70 M ShootAndSpeak.xcodeproj
|
211
|
+
|
212
|
+
... Figure out what's wrong ...
|
213
|
+
|
214
|
+
(4f12091...|BISECTING) $ git bisect reset
|
215
|
+
Switched to branch "master"
|
216
|
+
|
217
|
+
</pre>
|
218
|
+
|
219
|
+
<p>I hope you learn to use and love git-bisect. It’s really helped me when trying to find the cause of nasty bugs that seemingly came out of nowhere.</p>
|
220
|
+
|
221
|
+
|
222
|
+
<h3>Update</h3>
|
223
|
+
|
224
|
+
|
225
|
+
<p>The above is too much work. While searching the <a href="http://desprofundis.blogspot.com/2008/06/git-bisect-instructions.html">net</a>, I found something even easier and faster. You can start git-bisect with the commit hashes like so <code>git bisect start bad_commit good_commit</code>. Even better, you can then tell git-bisect to run the tests itself… this is where things get awesome: <code>git bisect run some_test</code>. It’ll iterate through your commits until it finds the bad one. Checkout the sample session below.</p>
|
226
|
+
|
227
|
+
|
228
|
+
<pre>
|
229
|
+
/tmp/fake(master) $ git bisect start 68d5ab7a61a871fd097d8820e248cfd168395e4e 20cbc038973d6c78805bc8bfc3d187c2b537f183
|
230
|
+
Bisecting: 1 revisions left to test after this
|
231
|
+
[3da37ed0ee87c9129a61142ecefef17ab0de7f0f] Test works
|
232
|
+
|
233
|
+
/tmp/fake(3da37ed...|BISECTING) $ git bisect run testrb test/unit/some_test.rb -n test_truth
|
234
|
+
running testrb test/unit/some_test.rb -n test_truth
|
235
|
+
Loaded suite some_test.rb
|
236
|
+
Started
|
237
|
+
.
|
238
|
+
Finished in 0.000338 seconds.
|
239
|
+
|
240
|
+
1 tests, 1 assertions, 0 failures, 0 errors
|
241
|
+
Bisecting: 0 revisions left to test after this
|
242
|
+
[765d7e5c4eba730078907fc00121b8b35ada64b0] Test fails
|
243
|
+
running testrb test/unit/some_test.rb -n test_truth
|
244
|
+
Loaded suite some_test.rb
|
245
|
+
Started
|
246
|
+
F
|
247
|
+
Finished in 0.009607 seconds.
|
248
|
+
|
249
|
+
1) Failure:
|
250
|
+
test_truth(ThisTest) [./test/unit/some_test.rb:5]:
|
251
|
+
&lt;false&gt; is not true.
|
252
|
+
|
253
|
+
1 tests, 1 assertions, 1 failures, 0 errors
|
254
|
+
765d7e5c4eba730078907fc00121b8b35ada64b0 is first bad commit
|
255
|
+
commit 765d7e5c4eba730078907fc00121b8b35ada64b0
|
256
|
+
Author: Trotter Cashion &lt;cashion@example.com&gt;
|
257
|
+
Date: Sun Sep 28 13:13:09 2008 -0400
|
258
|
+
|
259
|
+
Test fails
|
260
|
+
|
261
|
+
:040000 040000 167dd04f2b4101ea256a7a6525859bc03e5433d3 0b062d4b5b03e7ac51ac4050fc7397c8983a2f13 M test
|
262
|
+
bisect run success
|
263
|
+
|
264
|
+
/tmp/fake(765d7e5...|BISECTING) $
|
265
|
+
|
266
|
+
</pre>
|
267
|
+
|
268
|
+
<p>As you can see above, I’m using ruby for this set of tests. My preferred method is to have git-bisect run testrb (or spec) and specify a single test for it to execute. This ensures that everything runs quite quickly.</p>
|
269
|
+
|
270
|
+
|
271
|
+
<h3>Land Here!</h3>
|
272
|
+
|
273
|
+
|
274
|
+
<p>If you jumped here from the top, I regret to inform you that this post will make you very sad. Turn back now before you’re stuck improving your life by <a href="http://git.or.cz/#download">installing git</a>. Once you’ve installed git, go check out <a href="http://peepcode.com/">Peepcode</a>. They’ve got a really good <a href="https://peepcode.com/products/git">screencast</a> and <a href="https://peepcode.com/products/git-internals-pdf">pdf</a> that explain git excellently.</p>
|
275
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/9/27/git-bisect-is-your-new-best-friend</feedburner:origLink></entry>
|
276
|
+
<entry xml:base="http://trottercashion.com/">
|
277
|
+
<author>
|
278
|
+
<name>trotter</name>
|
279
|
+
</author>
|
280
|
+
<id>tag:trottercashion.com,2008-08-08:31</id>
|
281
|
+
<published>2008-08-08T11:35:00Z</published>
|
282
|
+
<updated>2008-08-08T11:36:11Z</updated>
|
283
|
+
<category term="javascript" />
|
284
|
+
<category term="mocks" />
|
285
|
+
<category term="screw-unit" />
|
286
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/359351596/mocking-screw-unit" rel="alternate" type="text/html" />
|
287
|
+
<title>Mocking Screw-Unit</title>
|
288
|
+
<content type="html">
|
289
|
+
<p>I’m big on tests. Unit testing helps me clarify my thinking on problems and ensure that my code works well. When writing tests, it’s essential to have a good mocking framework to separate the things you are testing from the things you are not. In Ruby, I like using flexmock for Test::Unit and rSpec’s built in mocking framework when using it. In Javascript though, screw-unit doesn’t really come with a way to mock by default. (As an aside, screw-unit totally rocks for testing js.)</p>
|
290
|
+
|
291
|
+
|
292
|
+
<p>Thankfully, my coworker <a href="http://github.com/tobowers">Topper</a> (who’s a kickass dev, btw), has been playing around with adding mocking to screw-unit. He’s got a <a href="http://github.com/tobowers/screw-unit/tree/master">fork</a> on github, docs at the previous link, and a quick <a href="http://blog.toppingdesign.com/2008/07/22/screw-unit-javascript-mocking/">example blog post</a>. Click through and check this shit out, cause it’s hot.</p>
|
293
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/8/8/mocking-screw-unit</feedburner:origLink></entry>
|
294
|
+
<entry xml:base="http://trottercashion.com/">
|
295
|
+
<author>
|
296
|
+
<name>trotter</name>
|
297
|
+
</author>
|
298
|
+
<id>tag:trottercashion.com,2008-07-07:27</id>
|
299
|
+
<published>2008-07-07T17:19:00Z</published>
|
300
|
+
<updated>2008-07-07T17:21:30Z</updated>
|
301
|
+
<category term="floats" />
|
302
|
+
<category term="ruby" />
|
303
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/329041849/floating-pain" rel="alternate" type="text/html" />
|
304
|
+
<title>Floating Pain</title>
|
305
|
+
<content type="html">
|
306
|
+
<p><a href="http://blog.toppingdesign.com/">Topper</a> mentioned a tweet he saw to me in which someone asked why <code>(4.6 * 100).to_i #=&gt; 459</code>. Though this seems like a ruby bug, it’s really just one of the annoying things you hit with rounding errors and floats. At issue is that <code>#to_i</code> floors the float, instead of rounding it. Since the value may be approximated at 459.999999, the <code>#to_i</code> floors it to 459. To have things work like you’d expect, use <code>#round</code> when converting <code>Float</code> to <code>Fixnum</code>. See below for some code examples:</p>
|
307
|
+
|
308
|
+
|
309
|
+
<pre class="ruby">
|
310
|
+
4.6.to_i # =&gt; 4
|
311
|
+
4.6.round # =&gt; 5
|
312
|
+
(4.6 * 100).to_i # =&gt; 459
|
313
|
+
(4.6 * 100).round # =&gt; 460
|
314
|
+
</pre>
|
315
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/7/7/floating-pain</feedburner:origLink></entry>
|
316
|
+
<entry xml:base="http://trottercashion.com/">
|
317
|
+
<author>
|
318
|
+
<name>trotter</name>
|
319
|
+
</author>
|
320
|
+
<id>tag:trottercashion.com,2008-06-27:26</id>
|
321
|
+
<published>2008-06-27T03:06:00Z</published>
|
322
|
+
<updated>2008-06-27T03:07:05Z</updated>
|
323
|
+
<category term="programming" />
|
324
|
+
<category term="trotter cashion" />
|
325
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/321023468/how-i-got-started-programming" rel="alternate" type="text/html" />
|
326
|
+
<title>How I Got Started Programming</title>
|
327
|
+
<content type="html">
|
328
|
+
<p><a href="http://www.pauldix.net/2008/06/how-i-got-start.html">Paul</a> says I’ve got to do this, and I don’t want to let him
|
329
|
+
down. <a href="http://gilesbowkett.blogspot.com/2008/06/how-i-got-started-programming.html">Giles</a> tagged him first, so you should probably read his too.</p>
|
330
|
+
|
331
|
+
|
332
|
+
<h3>How old were you when you started programming?</h3>
|
333
|
+
|
334
|
+
|
335
|
+
<p>In third grade (when I was 8) I started started taking super nerd math classes
|
336
|
+
with other super nerds. As part of those classes, they had us programming a
|
337
|
+
turtle to draw things on the screen. <a href="http://en.wikipedia.org/wiki/Logo_(programming_language">Logo</a>) was totally awesome and had me hooked
|
338
|
+
on the magic of programming.</p>
|
339
|
+
|
340
|
+
|
341
|
+
<h3>How did you get started programming?</h3>
|
342
|
+
|
343
|
+
|
344
|
+
<p>After Logo, my dad (he was a <span class="caps">CTO</span> at the time) bought me Visual Studio and a few
|
345
|
+
books on Visual Basic. It was lots of “Teach Yourself X in X days”, and I ran
|
346
|
+
through VB, C++, and a little Delphi. Naturally, those books didn’t teach me to
|
347
|
+
actually be good, though I did figure out how to make a few small games that I
|
348
|
+
could play.</p>
|
349
|
+
|
350
|
+
|
351
|
+
<p>My dad was a <span class="caps">CTO</span> at an investment bank, which is the kind of place that treats a
|
352
|
+
<span class="caps">CTO</span> like crap. I didn’t want to be the guy that got shat on, so in high school I
|
353
|
+
dropped programming and started learning businessy things. I even picked my
|
354
|
+
college based on the strength of its business school. Once I showed up, I
|
355
|
+
realized that I didn’t like anyone at the business school, that philosophy was
|
356
|
+
fun, and that math/econ could make me money. I promptly switched my major.</p>
|
357
|
+
|
358
|
+
|
359
|
+
<p>After college, I went to work at a job that I ended up hating, quit it, took a
|
360
|
+
few months to figure out my life, and realized that I really loved programming.
|
361
|
+
Thankfully I got lucky and read a <a href="http://lifehacker.com/software/notag/ruby-on-rails-book-as-a-download-105594.php">blog post</a> that tipped me to
|
362
|
+
the beta book of <span class="caps">AWDWR</span>, which taught me a lot about <strong>real</strong> programming. I
|
363
|
+
consider that the start of me becomming a real programmer, and not just some kid
|
364
|
+
that can code.</p>
|
365
|
+
|
366
|
+
|
367
|
+
<h3>What was your first language?</h3>
|
368
|
+
|
369
|
+
|
370
|
+
<p>Logo! Drawing with turtles rocks so hard. After that it was VB, which let me
|
371
|
+
push Windows around and made me a little cash.</p>
|
372
|
+
|
373
|
+
|
374
|
+
<h3>What was the first real program you wrote?</h3>
|
375
|
+
|
376
|
+
|
377
|
+
<p>I wrote my first useful program while working as an intern at a financial
|
378
|
+
services firm. The company was using <a href="http://www.advent.com/solutions/asset_managers/axys_platform">Axys</a> (really bad website alert!) for portfolio analysis and had a
|
379
|
+
tedious process for reconciling their branches with the back office. I wrote a
|
380
|
+
VB program that helped them to perform these reconciliations more quickly, which
|
381
|
+
I hear they still use.</p>
|
382
|
+
|
383
|
+
|
384
|
+
<h3>What languages have you used since you started programming?</h3>
|
385
|
+
|
386
|
+
|
387
|
+
<p>Logo, VB, C, C++, Delphi, Ruby, Objective-C, Erlang, Scheme, Javascript, Java, and maybe something else. Of those, I’d feel comfortable working on a project using Ruby, Javascript, Objective-C, or Erlang. I’m skilled enough in some of the others, but have vowed to never use them again. I’ll let you guess which.</p>
|
388
|
+
|
389
|
+
|
390
|
+
<h3>What was your first professional programming gig?</h3>
|
391
|
+
|
392
|
+
|
393
|
+
<p>2005 at the Nathan Kline Institute for Mental Health. There was a PhD there who
|
394
|
+
needed <a href="http://en.wikipedia.org/wiki/ImageJ">ImageJ</a> to talk to his microscope over a serial port and to have a
|
395
|
+
lot of old scripts from ObjectImage translated into ImageJ. It was a fun job
|
396
|
+
that let me work at my own pace and play a lot with the art of programming.</p>
|
397
|
+
|
398
|
+
|
399
|
+
<h3>If there is one thing you learned along the way that you would tell new
|
400
|
+
developers, what would it be?</h3>
|
401
|
+
|
402
|
+
|
403
|
+
<p>Surround yourself with great people, and never be the smartest guy in the room. If you’re lucky enough to work at a
|
404
|
+
company with some great programmers, you’ll learn a whole lot that way. If
|
405
|
+
your company is full of 9-5 coders, join a local developer group or start your
|
406
|
+
own. <a href="http://nycruby.org">Nyc.rb</a> and <a href="http://www.phillyonrails.org/">Philly On Rails</a> totally rock, so you could always move to New York or Philly and learn from some of the best.</p>
|
407
|
+
|
408
|
+
|
409
|
+
<h3>What’s the most fun you’ve ever had programming?</h3>
|
410
|
+
|
411
|
+
|
412
|
+
<p>Logo. I used to love making that little turtle draw all sorts of fun things on
|
413
|
+
the screen. There was no real need to make the turtle do things, I was doing it
|
414
|
+
just for the joy of it. I managed to recreate some of that feeling when working
|
415
|
+
on <a href="http://rubyforge.org/projects/spec-unit/">spec-unit</a>, which is really my only useful open source contribution to
|
416
|
+
date. Unfortunately, it only has one release ever, and I haven’t messed with it in two years.</p>
|
417
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/6/27/how-i-got-started-programming</feedburner:origLink></entry>
|
418
|
+
<entry xml:base="http://trottercashion.com/">
|
419
|
+
<author>
|
420
|
+
<name>trotter</name>
|
421
|
+
</author>
|
422
|
+
<id>tag:trottercashion.com,2008-06-22:5</id>
|
423
|
+
<published>2008-06-22T22:23:00Z</published>
|
424
|
+
<updated>2008-06-22T22:44:05Z</updated>
|
425
|
+
<category term="business" />
|
426
|
+
<category term="working remotely" />
|
427
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/317690654/on-working-remotely" rel="alternate" type="text/html" />
|
428
|
+
<title>On Working Remotely</title>
|
429
|
+
<content type="html">
|
430
|
+
<p>Being on my honeymoon in St. Barths has got me thinking a bit about working remotely. Don’t worry, I’m not working on this trip. I’m more trying to think if ways I could stay on this trip indefinitely, but still manage to make some cash.</p>
|
431
|
+
|
432
|
+
|
433
|
+
<p>I’m no stranger to remote work. As some of you may know, I live in Philly but continue to work for <a href="http://www.motionbox.com">Motionbox</a> in New York. I commute two days per week, and spend the rest working from home in Philly. Over the past year, I’ve started to catalog my likes and dislikes with this arrangement, and I’m going to list some of my dislikes here along with some possible ways to improve things. For now, we’ll just look at the first problem I’ve encountered:</p>
|
434
|
+
|
435
|
+
|
436
|
+
<h3>You’re out of touch</h3>
|
437
|
+
|
438
|
+
|
439
|
+
<p>If you’re distributed while the rest of the team is collocated, you <strong>will</strong> be out of the loop. When your boss is walking around the office and stopping at various desks, he won’t be stopping at yours. If you’re looking to be recognized for your accomplishments, this can be a major problem. It’s difficult to advance in the company if you’re not visible.</p>
|
440
|
+
|
441
|
+
|
442
|
+
<p>To combat this problem, I’ve found getting everyone on IM and <span class="caps">IRC</span> to be very helpful. If your company uses an open office, you’re in luck. The noise from the floor plan typically causes people to use headphones, so they’ll be much more prone to use IM and <span class="caps">IRC</span> for all their communication (even with those people right next to them). Another good technique is to send copious amounts of email. If people are cataloging what should be done and who has done what through email (a good practice regardless), then it’s much easier for you to keep track of what is happening in the office.</p>
|
443
|
+
|
444
|
+
|
445
|
+
<p>I don’t think that Skype or frequent phone calls help much in this regard. Typically, you’re only talking to one person and all the speaker phone arrangements I’ve seen aren’t that great. Voice is great for quickly hashing out the details of a plan with one other person, but is terrible as a mechanism for keeping up with the goings on of the company.</p>
|
446
|
+
|
447
|
+
|
448
|
+
<p>Making time (and spending the money) to get to the office at least once a month is invaluable. Though email, IM, and <span class="caps">IRC</span> help, they’re not a real substitute for quality time in person. One of the most important things I’ve done at Motionbox is to know when people are going out for after work drinks to celebrate various accomplishments and made sure that I was able to be in town for them. Though it sounds silly to talk about drinking as an important part of work, the main thing you miss by being remote is the social component. It’s much more important to get in town to socialize than it is to do actual work. You’ll have plenty of time to work when you’re home alone the next day.</p>
|
449
|
+
|
450
|
+
|
451
|
+
<p>Anyone have any thoughts on other ways to keep in touch while working remotely?</p>
|
452
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/6/22/on-working-remotely</feedburner:origLink></entry>
|
453
|
+
<entry xml:base="http://trottercashion.com/">
|
454
|
+
<author>
|
455
|
+
<name>trotter</name>
|
456
|
+
</author>
|
457
|
+
<id>tag:trottercashion.com,2008-06-11:20</id>
|
458
|
+
<published>2008-06-11T02:30:00Z</published>
|
459
|
+
<updated>2008-06-11T02:46:16Z</updated>
|
460
|
+
<category term="belongstodemeter" />
|
461
|
+
<category term="rails" />
|
462
|
+
<category term="ruby" />
|
463
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/309293334/belongstodemeter" rel="alternate" type="text/html" />
|
464
|
+
<title>BelongsToDemeter</title>
|
465
|
+
<content type="html">
|
466
|
+
<p>While playing with Rails the other day, I thought it would be fun if you could get at attributes of a belongs_to association without having to do the whole traverse association and check for nil thing.</p>
|
467
|
+
|
468
|
+
<pre class="ruby">
|
469
|
+
|
470
|
+
# Something like...
|
471
|
+
@person.group_name # => "Pizza Fans!" || nil
|
472
|
+
|
473
|
+
# Instead of...
|
474
|
+
@person.group ? @person.group.name : nil # => "Pizza Fans!" || nil
|
475
|
+
|
476
|
+
</pre>
|
477
|
+
|
478
|
+
<p>Thinking this would be a fun chance to play with some meta, I threw together <a href="http://github.com/trotter/belongs_to_demeter/tree/master">BelongsToDemeter</a>, which you can find over on <a href="http://github.com">GitHub</a>. It's a rails plugin, but don't expect it to actually install using script/plugin. The code is complete and utter crap, so it's probably best that Rails won't install it. It is slow, and most likely prone to error. Still, it's a fun little thought experiment, and I may decide to clean it up then speed it up if someone tells me they like it.</p>
|
479
|
+
|
480
|
+
<p>It does what I explained above and also lets you do fun things like this, which I think are useful when assigning associations through a form:</p>
|
481
|
+
|
482
|
+
<pre class="ruby">
|
483
|
+
|
484
|
+
# Lookup the user 'Bob' by login and assign
|
485
|
+
# it to the user association
|
486
|
+
@character.user # => nil
|
487
|
+
@character.user_login = "bob"
|
488
|
+
@character.user.login # => "bob"
|
489
|
+
|
490
|
+
</pre>
|
491
|
+
|
492
|
+
<p>Anyway, go over to <a href="http://github.com">GitHub</a> and check out <a href="http://github.com/trotter/belongs_to_demeter/tree/master">BelongsToDemeter</a>. When you're done, let me know if you like the concept. After all that, go erase all memory of the implementation details from your mind, they're ugly.</p>
|
493
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/6/11/belongstodemeter</feedburner:origLink></entry>
|
494
|
+
<entry xml:base="http://trottercashion.com/">
|
495
|
+
<author>
|
496
|
+
<name>trotter</name>
|
497
|
+
</author>
|
498
|
+
<id>tag:trottercashion.com,2008-06-05:16</id>
|
499
|
+
<published>2008-06-05T02:22:00Z</published>
|
500
|
+
<updated>2008-06-11T02:48:00Z</updated>
|
501
|
+
<category term="macro tests" />
|
502
|
+
<category term="rspec" />
|
503
|
+
<category term="ruby" />
|
504
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/304990208/macro-tests" rel="alternate" type="text/html" />
|
505
|
+
<title>Macro Tests</title>
|
506
|
+
<content type="html">
|
507
|
+
<p>I went to <a href="http://blog.hasmanythrough.com/">Josh Susser</a>'s talk at RailsConf last week, where he mentioned that no one really writes macro tests with rSpec. Naturally, I found this to be a true shame, since I think macro tests are a great way to clean up your code. What's a macro test you ask? Sit right there and I'll tell you.</p>
|
508
|
+
|
509
|
+
<p>A macro test is a test that defines other tests for you. It's a great way to reduce repetition in your test code, thereby making it easier to read. Take these three tests as an example:<p>
|
510
|
+
|
511
|
+
<pre class="ruby">
|
512
|
+
describe 'GET /users/:user_id/posts/:post_id/comments' do
|
513
|
+
it 'should be a success' do
|
514
|
+
get :index, :user_id =&gt; 2, :post_id =&gt; 3
|
515
|
+
response.should be_success
|
516
|
+
end
|
517
|
+
|
518
|
+
it 'should 404 without a user id' do
|
519
|
+
get :index, :post_id =&gt; 3
|
520
|
+
response.headers['Status'].to_i.should == 404
|
521
|
+
end
|
522
|
+
|
523
|
+
it 'should 404 without a post id' do
|
524
|
+
get :index, :user_id =&gt; 2
|
525
|
+
response.headers['Status'].to_i.should == 404
|
526
|
+
end
|
527
|
+
end
|
528
|
+
</pre>
|
529
|
+
|
530
|
+
<p>Obviously there's a little bit of duplication in the 404's. Let's define macro tests to clean that up.</p>
|
531
|
+
|
532
|
+
<pre class="ruby">
|
533
|
+
describe 'GET /users/:user_id/posts/:post_id/comments' do
|
534
|
+
def self.should_404_without(param)
|
535
|
+
# Since you're calling this method within the describe block,
|
536
|
+
# it is being called within the correct context.
|
537
|
+
it "should 404 without #{param}" do
|
538
|
+
get :index, paramz.merge(param.to_sym =&gt; nil)
|
539
|
+
response.headers['Status'].to_i.should == 404
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
should_404_without "user_id"
|
544
|
+
should_404_without "post_id"
|
545
|
+
|
546
|
+
# Normally I put this at the bottom, only here to keep it close to the above.
|
547
|
+
# This method is called by the test the macro test defines to hand off the params.
|
548
|
+
def paramz
|
549
|
+
{:user_id =&gt; 2, :post_id =&gt; 3}
|
550
|
+
end
|
551
|
+
|
552
|
+
it 'should be a success' do
|
553
|
+
get :index, paramz
|
554
|
+
response.should be_success
|
555
|
+
end
|
556
|
+
end
|
557
|
+
</pre>
|
558
|
+
|
559
|
+
<p>Hmm... this doesn't really look any cleaner. The problem is that we did this for only one action. If we generalize should_404_without more, then we can put it into its own shared example set that we can include in any describe block. Let's look at that now.</p>
|
560
|
+
|
561
|
+
<pre class="ruby">
|
562
|
+
shared_examples_for "controllers" do
|
563
|
+
def self.should_normally_succeed
|
564
|
+
it 'should be a success' do
|
565
|
+
get @action, paramz
|
566
|
+
response.should be_success
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
def self.should_404_without(param)
|
571
|
+
it "should 404 without #{param}" do
|
572
|
+
get @action, paramz.merge(param.to_sym =&gt; nil)
|
573
|
+
response.headers['Status'].to_i.should == 404
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
describe 'GET /users/:user_id/posts/:post_id/comments' do
|
579
|
+
it_should_behave_like "controllers"
|
580
|
+
|
581
|
+
before(:each) { @action = :index }
|
582
|
+
|
583
|
+
should_normally_succeed
|
584
|
+
should_404_without "user_id"
|
585
|
+
should_404_without "post_id"
|
586
|
+
|
587
|
+
def paramz
|
588
|
+
{:user_id =&gt; 2, :post_id =&gt; 3}
|
589
|
+
end
|
590
|
+
end
|
591
|
+
</pre>
|
592
|
+
|
593
|
+
<p>Oh snap! That describe block is a lot cleaner, and we can tuck the shared examples into spec_helper.rb to really clean things up. Using macro tests, you'll find it's very easy to create a lot of REST controllers very quickly. In fact, I plan on open sourcing something to help with that very soon...</p>
|
594
|
+
|
595
|
+
<p><strong>Update</strong>: See the comments for David's advice on pulling this out of shared examples and into a module that you can then add to rSpec. This cleans up the describe blocks even more. Mega win!</p>
|
596
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/6/5/macro-tests</feedburner:origLink></entry>
|
597
|
+
<entry xml:base="http://trottercashion.com/">
|
598
|
+
<author>
|
599
|
+
<name>trotter</name>
|
600
|
+
</author>
|
601
|
+
<id>tag:trottercashion.com,2008-05-04:12</id>
|
602
|
+
<published>2008-05-04T04:14:00Z</published>
|
603
|
+
<updated>2008-05-04T14:39:31Z</updated>
|
604
|
+
<category term="moving" />
|
605
|
+
<category term="starting" />
|
606
|
+
<link href="http://feeds.feedburner.com/~r/trottercashion/~3/283393385/starting-the-car" rel="alternate" type="text/html" />
|
607
|
+
<title>Starting the Car</title>
|
608
|
+
<content type="html">
|
609
|
+
<p>Well, it's not much of a start, but every trip must have a beginning. As I wake up and wipe my bleary eyes, I'm going to slowly pull this car out of the driveway and put it on the road. The tunes aren't yet cranking; no Allman Brothers is yet helping us greet the open road. Still, we've left that old home that is <a href="http://lifecoding.com">lifecoding</a> and are starting out on something new, something fresh. We're running on rubies with a comfortable walking shoe pushing the accelerator. I hope you enjoy this ride.</p>
|
610
|
+
</content> <feedburner:origLink>http://trottercashion.com/2008/5/4/starting-the-car</feedburner:origLink></entry>
|
611
|
+
</feed>
|