feedjira 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +162 -0
- data/Gemfile +17 -0
- data/Guardfile +5 -0
- data/README.md +242 -0
- data/Rakefile +6 -0
- data/benchmarks/README.md +90 -0
- data/benchmarks/basic.rb +31 -0
- data/benchmarks/feed_list.txt +10 -0
- data/benchmarks/feed_xml/apple.xml +149 -0
- data/benchmarks/feed_xml/cnn.xml +278 -0
- data/benchmarks/feed_xml/daring_fireball.xml +1697 -0
- data/benchmarks/feed_xml/engadget.xml +604 -0
- data/benchmarks/feed_xml/feedjira_commits.xml +370 -0
- data/benchmarks/feed_xml/gizmodo.xml +2 -0
- data/benchmarks/feed_xml/loop.xml +441 -0
- data/benchmarks/feed_xml/rails.xml +1938 -0
- data/benchmarks/feed_xml/white_house.xml +951 -0
- data/benchmarks/feed_xml/xkcd.xml +2 -0
- data/benchmarks/fetching_systems.rb +23 -0
- data/benchmarks/other_libraries.rb +73 -0
- data/feedjira.gemspec +27 -0
- data/lib/feedjira.rb +16 -0
- data/lib/feedjira/core_ext.rb +3 -0
- data/lib/feedjira/core_ext/date.rb +19 -0
- data/lib/feedjira/core_ext/string.rb +9 -0
- data/lib/feedjira/core_ext/time.rb +31 -0
- data/lib/feedjira/feed.rb +459 -0
- data/lib/feedjira/feed_entry_utilities.rb +66 -0
- data/lib/feedjira/feed_utilities.rb +103 -0
- data/lib/feedjira/parser.rb +20 -0
- data/lib/feedjira/parser/atom.rb +61 -0
- data/lib/feedjira/parser/atom_entry.rb +34 -0
- data/lib/feedjira/parser/atom_feed_burner.rb +22 -0
- data/lib/feedjira/parser/atom_feed_burner_entry.rb +35 -0
- data/lib/feedjira/parser/google_docs_atom.rb +28 -0
- data/lib/feedjira/parser/google_docs_atom_entry.rb +29 -0
- data/lib/feedjira/parser/itunes_rss.rb +50 -0
- data/lib/feedjira/parser/itunes_rss_item.rb +41 -0
- data/lib/feedjira/parser/itunes_rss_owner.rb +12 -0
- data/lib/feedjira/parser/rss.rb +24 -0
- data/lib/feedjira/parser/rss_entry.rb +37 -0
- data/lib/feedjira/parser/rss_feed_burner.rb +23 -0
- data/lib/feedjira/parser/rss_feed_burner_entry.rb +43 -0
- data/lib/feedjira/version.rb +3 -0
- data/spec/feedjira/feed_entry_utilities_spec.rb +62 -0
- data/spec/feedjira/feed_spec.rb +762 -0
- data/spec/feedjira/feed_utilities_spec.rb +273 -0
- data/spec/feedjira/parser/atom_entry_spec.rb +86 -0
- data/spec/feedjira/parser/atom_feed_burner_entry_spec.rb +47 -0
- data/spec/feedjira/parser/atom_feed_burner_spec.rb +56 -0
- data/spec/feedjira/parser/atom_spec.rb +76 -0
- data/spec/feedjira/parser/google_docs_atom_entry_spec.rb +22 -0
- data/spec/feedjira/parser/google_docs_atom_spec.rb +31 -0
- data/spec/feedjira/parser/itunes_rss_item_spec.rb +63 -0
- data/spec/feedjira/parser/itunes_rss_owner_spec.rb +18 -0
- data/spec/feedjira/parser/itunes_rss_spec.rb +58 -0
- data/spec/feedjira/parser/rss_entry_spec.rb +85 -0
- data/spec/feedjira/parser/rss_feed_burner_entry_spec.rb +85 -0
- data/spec/feedjira/parser/rss_feed_burner_spec.rb +57 -0
- data/spec/feedjira/parser/rss_spec.rb +57 -0
- data/spec/sample_feeds/AmazonWebServicesBlog.xml +797 -0
- data/spec/sample_feeds/AmazonWebServicesBlogFirstEntryContent.xml +63 -0
- data/spec/sample_feeds/AtomFeedWithSpacesAroundEquals.xml +61 -0
- data/spec/sample_feeds/FeedBurnerUrlNoAlternate.xml +28 -0
- data/spec/sample_feeds/GoogleDocsList.xml +188 -0
- data/spec/sample_feeds/HREFConsideredHarmful.xml +314 -0
- data/spec/sample_feeds/HREFConsideredHarmfulFirstEntry.xml +22 -0
- data/spec/sample_feeds/ITunesWithSpacesInAttributes.xml +63 -0
- data/spec/sample_feeds/PaulDixExplainsNothing.xml +175 -0
- data/spec/sample_feeds/PaulDixExplainsNothingAlternate.xml +175 -0
- data/spec/sample_feeds/PaulDixExplainsNothingFirstEntryContent.xml +19 -0
- data/spec/sample_feeds/PaulDixExplainsNothingWFW.xml +174 -0
- data/spec/sample_feeds/SamRuby.xml +583 -0
- data/spec/sample_feeds/TechCrunch.xml +1515 -0
- data/spec/sample_feeds/TechCrunchFirstEntry.xml +9 -0
- data/spec/sample_feeds/TechCrunchFirstEntryDescription.xml +3 -0
- data/spec/sample_feeds/TenderLovemaking.xml +516 -0
- data/spec/sample_feeds/TenderLovemakingFirstEntry.xml +66 -0
- data/spec/sample_feeds/TrotterCashionHome.xml +611 -0
- data/spec/sample_feeds/TypePadNews.xml +368 -0
- data/spec/sample_feeds/atom_with_link_tag_for_url_unmarked.xml +31 -0
- data/spec/sample_feeds/itunes.xml +67 -0
- data/spec/sample_feeds/pet_atom.xml +497 -0
- data/spec/spec_helper.rb +88 -0
- metadata +229 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
<p>Oops! When I released <a href="http://nokogiri.rubyforge.org/" onclick="javascript:urchinTracker ('/outbound/article/nokogiri.rubyforge.org');">nokogiri</a> version 1.0.7, I totally forgot to talk about Nokogiri::Slop() feature that was added. Why is it called "slop"? It lets you sloppily explore documents. Basically, it decorates your document with method_missing() that allows you to search your document via method calls.</p>
|
2
|
+
<p>Given this document:</p>
|
3
|
+
<div class="codesnip-container" >
|
4
|
+
<div class="codesnip" style="font-family: monospace;">doc = Nokogiri::Slop<span class="br0">(</span><<-eohtml<span class="br0">)</span><br />
|
5
|
+
<html><br />
|
6
|
+
<body><br />
|
7
|
+
<p>hello</p><br />
|
8
|
+
<p <span class="kw1">class</span>=<span class="st0">"bold"</span>>bold hello</p><br />
|
9
|
+
<body><br />
|
10
|
+
</html><br />
|
11
|
+
eohtml</div>
|
12
|
+
</div>
|
13
|
+
<p>You may look through the tree like so:</p>
|
14
|
+
<div class="codesnip-container" >
|
15
|
+
<div class="codesnip" style="font-family: monospace;">doc.<span class="me1">html</span>.<span class="me1">body</span>.<span class="kw3">p</span><span class="br0">(</span>'.<span class="me1">bold</span>'<span class="br0">)</span>.<span class="me1">text</span> <span class="co1"># => 'bold hello' </span></div>
|
16
|
+
</div>
|
17
|
+
<p>The way this works is that method missing is implemented on every node in the document tree. That method missing method creates an xpath or css query by using the method name and method arguments. This means that a new search is executed for every method call. It's fun for playing around, but you definitely won't get the same performance as using one specific CSS search.</p>
|
18
|
+
<p>My favorite part is that method missing is actually in the <a href="http://github.com/tenderlove/nokogiri/tree/master/lib/nokogiri/decorators/slop.rb" onclick="javascript:urchinTracker ('/outbound/article/github.com');">slop decorator</a>. When you use the Nokogiri::Slop() method, it adds the decorator to a list that gets mixed in to every node instance at runtime using Module#extend. That lets me have sweet method missing action, without actually putting method missing in my Node class.</p>
|
19
|
+
<p>Here is a simplified example:</p>
|
20
|
+
<div class="codesnip-container" >
|
21
|
+
<div class="codesnip" style="font-family: monospace;"><span class="kw1">module</span> Decorator<br />
|
22
|
+
<span class="kw1">def</span> method_a<br />
|
23
|
+
<span class="st0">"method a"</span><br />
|
24
|
+
<span class="kw1">end</span></p>
|
25
|
+
<p> <span class="kw1">def</span> method_b<br />
|
26
|
+
<span class="st0">"method b: #{super}"</span><br />
|
27
|
+
<span class="kw1">end</span><br />
|
28
|
+
<span class="kw1">end</span></p>
|
29
|
+
<p><span class="kw1">class</span> Foo<br />
|
30
|
+
<span class="kw1">def</span> method_b<br />
|
31
|
+
<span class="st0">"inside foo"</span><br />
|
32
|
+
<span class="kw1">end</span><br />
|
33
|
+
<span class="kw1">end</span></p>
|
34
|
+
<p>foo = Foo.<span class="me1">new</span><br />
|
35
|
+
foo.<span class="me1">extend</span><span class="br0">(</span>Decorator<span class="br0">)</span></p>
|
36
|
+
<p><span class="kw3">puts</span> foo.<span class="me1">method_a</span> <span class="co1"># => 'method a'</span><br />
|
37
|
+
<span class="kw3">puts</span> foo.<span class="me1">method_b</span> <span class="co1"># => 'method b: inside foo'</span></p>
|
38
|
+
<p>foo2 = Foo.<span class="me1">new</span><br />
|
39
|
+
<span class="kw3">puts</span> foo2.<span class="me1">method_b</span> <span class="co1"># => 'inside foo'</span><br />
|
40
|
+
<span class="kw3">puts</span> foo2.<span class="me1">method_a</span> <span class="co1"># => NoMethodError </span></div>
|
41
|
+
</div>
|
42
|
+
<p>Module#extend is used to add functionality to the <strong>instance</strong> 'foo', but not 'foo2'. Both 'foo' and 'foo2' are instances of Foo, but using Module#extend, we can conditionally add functionality <strong>without monkey patching</strong> and keeping a clean separation of concerns. You can even reach previous functionality by calling super.</p>
|
43
|
+
<p>But wait! There's more! You can stack up these decorators as much as you want. For example:</p>
|
44
|
+
<div class="codesnip-container" >
|
45
|
+
<div class="codesnip" style="font-family: monospace;"><span class="kw1">module</span> AddAString<br />
|
46
|
+
<span class="kw1">def</span> method<br />
|
47
|
+
<span class="st0">"Added a string: #{super}"</span><br />
|
48
|
+
<span class="kw1">end</span><br />
|
49
|
+
<span class="kw1">end</span></p>
|
50
|
+
<p><span class="kw1">module</span> UpperCaseResults<br />
|
51
|
+
<span class="kw1">def</span> method<br />
|
52
|
+
<span class="kw1">super</span>.<span class="me1">upcase</span><br />
|
53
|
+
<span class="kw1">end</span><br />
|
54
|
+
<span class="kw1">end</span></p>
|
55
|
+
<p><span class="kw1">class</span> Foo<br />
|
56
|
+
<span class="kw1">def</span> method<br />
|
57
|
+
<span class="st0">"foo"</span><br />
|
58
|
+
<span class="kw1">end</span><br />
|
59
|
+
<span class="kw1">end</span></p>
|
60
|
+
<p>foo = Foo.<span class="me1">new</span><br />
|
61
|
+
foo.<span class="me1">extend</span><span class="br0">(</span>AddAString<span class="br0">)</span><br />
|
62
|
+
foo.<span class="me1">extend</span><span class="br0">(</span>UpperCaseResults<span class="br0">)</span></p>
|
63
|
+
<p><span class="kw3">puts</span> foo.<span class="me1">method</span> <span class="co1"># => 'ADDED A STRING: FOO' </span></div>
|
64
|
+
</div>
|
65
|
+
<p>Conditional functionality added to methods with no weird "alias method chain" involvement. Awesome!</p>
|
66
|
+
<p>I love ruby!</p>
|
@@ -0,0 +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>
|