Jekyll2021-04-27T09:46:22-07:00https://finagle.org/feed.xmlfinagleSteve SloanRuby Gotcha: Chained Assignment2015-12-30T00:00:00-08:002015-12-30T00:00:00-08:00https://finagle.org/2015/12/ruby-gotcha-chained-assignment<p>I ran into a tricky bug that involved an obscure bit of Ruby behavior, involving the chained assignment idiom and overloaded assignment operators.</p>
<h3 id="chained-assignment-considered-harmful">Chained Assignment Considered Harmful</h3>
<p>“Chained Assigned” refers to the idiom of using the value of an assignment expression in another assignment expression, e.g.:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a = b = 42
</code></pre></div></div>
<p>This seems simple enough, and it works fine in this case, but it’s an idiom that doesn’t always behave like you might expect. That is, you might expect it to be equivalent to:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>b = 42
a = b # WRONG
</code></pre></div></div>
<p>But it’s actually:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>b = 42
a = 42 # correct
</code></pre></div></div>
<h3 id="overloaded-assignment-operators-may-surprise-you">Overloaded assignment operators may surprise you</h3>
<p>Consider a <code class="language-plaintext highlighter-rouge">Person</code> class that, for some contrived reason, stores people’s names in uppercase:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Person
attr_reader :name
def name=(name)
@name = name.upcase
end
end
person = Person.new
name = person.name = "Bob"
</code></pre></div></div>
<p>You might expect both variables (<code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">person.name</code>) to have the same value (i.e. <code class="language-plaintext highlighter-rouge">"BOB"</code>), but after the assignment, <code class="language-plaintext highlighter-rouge">name</code> will be <code class="language-plaintext highlighter-rouge">"Bob"</code> and <code class="language-plaintext highlighter-rouge">person.name</code> will be <code class="language-plaintext highlighter-rouge">"BOB"</code>. That’s because …</p>
<blockquote>
<p><strong>Ruby ignores the return value of overloaded assignment operators</strong></p>
</blockquote>
<p>At least in that case the the behavior is consistent, if not obvious, but what about when we use conditional assignment for default values:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>name = (person.name ||= "Unknown")
</code></pre></div></div>
<p>The first time this expression runs, <code class="language-plaintext highlighter-rouge">name</code> will be set to the default (<code class="language-plaintext highlighter-rouge">"Unknown"</code>), but subsequent times it will be the the uppercase value in <code class="language-plaintext highlighter-rouge">person.name</code> (i.e. <code class="language-plaintext highlighter-rouge">"UNKNOWN"</code>).</p>
<h3 id="how-this-can-create-subtle-bugs">How this can create subtle bugs</h3>
<p>So far we’ve only looked at simple assignments of scalar values, but using Array or Hash values is where things can go very wrong. Consider a blog <code class="language-plaintext highlighter-rouge">Post</code> that has an array of tags. We want to make sure that each tag is a <code class="language-plaintext highlighter-rouge">String</code>, so the assignment operator maps the list and casts each value:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Post
attr_reader :tags
def tags=(tags)
@tags = tags.map { |tag| tag.to_s }
end
end
post = Post.new
tags = (post.tags ||= [])
tags += ['ruby', 'gotchas']
</code></pre></div></div>
<p>In this case, the conditional assignment means that <code class="language-plaintext highlighter-rouge">tags</code> will be set to the empty Array passed in the assignment, which is different than the Array (also empty) that’s returned by the <code class="language-plaintext highlighter-rouge">map</code> call in the assignment operator. Adding the tag strings to the array does nothing, because it’s not the array stored in the <code class="language-plaintext highlighter-rouge">Post</code>. Once <code class="language-plaintext highlighter-rouge">post.tags</code> is set, though, the conditional assignment does nothing, <code class="language-plaintext highlighter-rouge">tags</code> references the same Array as <code class="language-plaintext highlighter-rouge">post.tags</code>, and things work as expected.</p>
<p>If this seems like an extreme edge-case, you may have a point, but it’s also an actual bug that I ran into in a popular database library. Based on my new understanding on Ruby assignment, I’m inclined to avoid chained assignment all together, and just move the default assignment to its own line.</p>Steve SloanI ran into a tricky bug that involved an obscure bit of Ruby behavior, involving the chained assignment idiom and overloaded assignment operators.HTTP Caching Like an Idiot: Redux2015-12-26T00:00:00-08:002015-12-26T00:00:00-08:00https://finagle.org/2015/12/http-caching-like-an-idiot-redux<p>Apparently Steam didn’t read <a href="/2013/05/how-to-use-http-caching-like-an-idiot">my last blog post</a>, as they’ve discovered their own way to <a href="http://venturebeat.com/2015/12/25/steam-goes-down-as-technical-issues-reveal-some-private-info/">Use HTTP Caching Like an Idiot</a>. Come on, people, what’s the point of me making all these mistakes if you won’t learn from them?</p>Steve SloanApparently Steam didn’t read my last blog post, as they’ve discovered their own way to Use HTTP Caching Like an Idiot. Come on, people, what’s the point of me making all these mistakes if you won’t learn from them?How to Use HTTP Caching Like an Idiot2013-05-12T00:00:00-07:002013-05-12T00:00:00-07:00https://finagle.org/2013/05/how-to-use-http-caching-like-an-idiot<p>I’ve found that it’s pretty easy to run your own custom blog for with zero hosting cost, and even survive some significant traffic spikes, with liberal usage of <a href="http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-fresh_when">HTTP Caching in Rails</a>. So it’s somewhat ironic that when I come to update this blog this morning (after giving the last post a good year to really take root in the Collective Unconscious), I noticed that my caching fetish had shot me in the foot.</p>
<p>You see<sup>1</sup>, for logged-in users (i.e. me) the blog pages have an extra
panel in the sidebar for creating new posts. Since all of the blog pages are cached (with <code class="language-plaintext highlighter-rouge">last_modified</code> set to the update time of the most recent post), that admin panel was getting cached along with the rest of the page, and served to all public visitors (both of them). D’oh!</p>
<p>The quick solution was pretty simple:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fresh_when(...) unless current_user
</code></pre></div></div>
<p>That will at least keep from polluting the publicly-cached page with user- (and, especially, admin-) specific HTML. If that seems like such an obvious thing to do that only an idiot wouldn’t think to do it, then you’re probably reading the wrong blog.</p>
<p>For the rest of us, I’m contemplating more complicated (but safer!) solutions, to keep us from spamming the internet with our private data. The first step might just be a mechanism where the partial that renders users-specific HTML could just cancel the HTTP caching. A more scalable solution would involve <a href="http://guides.rubyonrails.org/caching_with_rails.html#fragment-caching">Fragment Caching</a> and <a href="http://memcached.org/">Memcached</a>. But part of me wants to go <a href="http://knowyourmeme.com/memes/full-retard">Full Paranoid</a> with some sort of Frankensteinian melding of <a href="http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/">SafeBuffers</a> and <a href="https://github.com/ryanb/cancan">CanCan</a>, so no string ever makes it to the response body unless it’s Certified Cache Safe.</p>
<p><a href="http://www.urbandictionary.com/define.php?term=You%20know%2C%20for%20kids">You know, for
idiots!</a></p>
<hr />
<p><sup>1</sup> That’s a retroactively-foreshadowing meta-pun. You’re welcome.</p>Steve SloanI’ve found that it’s pretty easy to run your own custom blog for with zero hosting cost, and even survive some significant traffic spikes, with liberal usage of HTTP Caching in Rails. So it’s somewhat ironic that when I come to update this blog this morning (after giving the last post a good year to really take root in the Collective Unconscious), I noticed that my caching fetish had shot me in the foot.Small Footprint MongoDB2012-03-26T00:00:00-07:002012-03-26T00:00:00-07:00https://finagle.org/2012/03/small-footprint-mongodb<p><a href="http://www.mongodb.org/">MongoDB</a> comes configured out of the box for maximum performance and reliability on production databases. But it can be a bit of a disk hog, and if you’re using a development environment with an <a href="http://en.wikipedia.org/wiki/Ssd">SSD</a> like me (which I highly recommend), disk space might be scarce. After doing a little research, I found configuration settings that significantly reduce MongoDB’s disk usage.</p>
<p>Edit your MongoDB configuration file (<code class="language-plaintext highlighter-rouge">/etc/mongod.conf</code>) and add some/all of the following:</p>
<h3 id="smallfiles--true"><code class="language-plaintext highlighter-rouge">smallfiles = true</code></h3>
<p>Uses smaller data file sizes — starting at 16MB instead of 128MB — and create fewer files initially. This can save almost 200MB on small collections (each!).</p>
<h3 id="oplogsize--100-mb"><code class="language-plaintext highlighter-rouge">oplogsize = 100</code> (MB)</h3>
<p>If you’re using an oplog for replication (or just for update notifications), you can set the size smaller than the default of “5% of all disk space”.</p>
<h3 id="nojournal--true"><code class="language-plaintext highlighter-rouge">nojournal = true</code></h3>
<p>MongoDB 2.0 introduced journaling, which is great for production environments, but not very useful in development. You can disable it and save several GB.</p>Steve SloanMongoDB comes configured out of the box for maximum performance and reliability on production databases. But it can be a bit of a disk hog, and if you’re using a development environment with an SSD like me (which I highly recommend), disk space might be scarce. After doing a little research, I found configuration settings that significantly reduce MongoDB’s disk usage.Excluding a bad RPM package2011-05-13T00:00:00-07:002011-05-13T00:00:00-07:00https://finagle.org/2011/05/excluding-a-bad-rpm-package<p>I’m a big fan of <a href="http://kde.org">KDE</a>, as both a user and a developer, and <a href="http://akregator.kde.org">Akregator</a> is my RSS feed reader of choice. I’m also a big fan of RSS feeds, using them for almost all my regular daily information consumption.</p>
<p>So imagine my notable lack of delight when, after doing a regular YUM update, I discovered that the latest version of Akregator has <a href="https://bugs.kde.org/show_bug.cgi?id=271149">a serious bug that makes it almost unusable</a>. And I didn’t even want the new version anyway.</p>
<p>Ah, but since I’m using RPM and YUM, the fix for this sort of thing is actually pretty simple, although it took me a few minutes of reading man pages to work it out, so I thought I should share the fruits of my labor. Here’s how you exclude a bad RPM package:</p>
<p>First, find the last good version of the appropriate package:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rpm -qf `which akregator`
kdepim-4.4.11.1-2.fc14.x86_64
$ sudo yum list kdepim --showduplicates
...
Installed Packages
kdepim.x86_64 7:4.4.11.1-2.fc14
Available Packages
kdepim.x86_64 6:4.4.6-2.fc14
kdepim.x86_64 7:4.4.11.1-2.fc14
</code></pre></div></div>
<p>In this case, the desired version is 4.4.6 (the “6:” is the epoch, the “–2” is the release, and the “fc14” is the architecture).</p>
<p>Next, downgrade the package. If there are any dependency errors, you’ll also need to downgrade those packages too.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo yum downgrade kdepim-4.4.6
...
Error: Package: 7:kdepim-libs-4.4.11.1-2.fc14.x86_64
...
$ sudo yum downgrade kdepim-4.4.6 kdepim-libs
...
Removed:
kdepim.x86_64 7:4.4.11.1-2.fc14 kdepim-libs.x86_64 7:4.4.11.1-2.fc14
Installed:
kdepim.x86_64 6:4.4.6-2.fc14 kdepim-libs.x86_64 6:4.4.6-2.fc14
Complete!
</code></pre></div></div>
<p>And finally, edit <code class="language-plaintext highlighter-rouge">/etc/yum.conf</code> and add the offending package version to an <code class="language-plaintext highlighter-rouge">exclude</code> line:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[main]
...
exclude=kdepim-4.4.11.1 kdepim-libs-4.4.11.1
</code></pre></div></div>
<p>Take that, <code class="language-plaintext highlighter-rouge">kdepims-4.4.11</code>. <strong><em>plonk</em></strong></p>Steve SloanI’m a big fan of KDE, as both a user and a developer, and Akregator is my RSS feed reader of choice. I’m also a big fan of RSS feeds, using them for almost all my regular daily information consumption.AudioBook Log2011-05-09T00:00:00-07:002011-05-09T00:00:00-07:00https://finagle.org/2011/05/audiobook-log<p>For over a year I’ve been commuting for almost 2 hours a day, and in an attempt to stave-off potentially lethal boredom, I’ve been passing the time listening to audiobooks. Here’s what I’ve been reading/listening
to:</p>
<p>Note, format is: <em>title</em> (<em>year</em>), <em>authors</em> (<em>readers</em>)</p>
<ul>
<li><a href="http://www.amazon.com/Daily-Show-Stewart-Presents-Earth/dp/044657922X">Earth: A Visitor’s Guide to the Human
Race</a>
(2010), Jon Stewart et al. (Jon Stewart)</li>
<li><a href="http://www.amazon.com/Freakonomics-Economist-Explores-Hidden-Everything/dp/0060731338">Freakonomics: A Rogue Economist Explores the Hidden Side of
Everything</a>
(2005), Steven D. Levitt and Stephen J. Dubner (Stephen J. Dubner)</li>
<li><a href="http://www.amazon.com/God-Not-Great-Religion-Everything/dp/0446697966">God Is Not Great: How Religion Poisons
Everything</a>
(2009), Christopher Hitchens (Christopher Hitchens)</li>
<li><a href="http://www.amazon.com/Fleet-Worlds-Larry-Niven/dp/0765357836">Fleet of
Worlds</a>
(2007), Larry Niven and Edward M. Lerner (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Juggler-Worlds-Larry-Niven/dp/0765357844">Juggler of
Worlds</a>
(2008), Larry Niven and Edward M. Lerner (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Destroyer-Worlds-Larry-Niven/dp/B004A14W9U">Destroyer of
Worlds</a>
(2009), Larry Niven and Edward M. Lerner (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Betrayer-Worlds-Larry-Niven/dp/0765326086">Betrayer of
Worlds</a>
(2010), Larry Niven and Edward M. Lerner (Tom Weiner)</li>
<li><a href="http://www.amazon.com/God-Delusion-Richard-Dawkins/dp/0618918248">The God
Delusion</a>
(2008), Richard Dawkins (Richard Dawkins)</li>
<li><a href="http://www.amazon.com/Greatest-Show-Earth-Evidence-Evolution/dp/1416594795">The Greatest Show on Earth: The Evidence for
Evolution</a>
(2010), Richard Dawkins (Richard Dawkins)</li>
<li><a href="http://www.amazon.com/Ancestors-Tale-Pilgrimage-Dawn-Evolution/dp/061861916X">The Ancestor’s Tale: A Pilgrimage to the Dawn of
Evolution</a>
(2005), Richard Dawkins (Richard Dawkins)</li>
<li><a href="http://www.amazon.com/End-Faith-Religion-Terror-Future/dp/0393035158">The End of Faith: Religion, Terror, and the Future of
Reason</a>
(2005), Sam Harris (Brian Emerson)</li>
<li><a href="http://www.amazon.com/Letter-Christian-Nation-Vintage-Harris/dp/0307278778">Letter to a Christian
Nation</a>
(2008), Sam Harris (Jordan Bridges)</li>
<li><a href="http://www.amazon.com/Moral-Landscape-Science-Determine-Values/dp/1439171211">The Moral Landscape: How Science Can Determine Human
Values</a>
(2010), Sam Harris (Sam Harris)</li>
<li><a href="http://www.amazon.com/Unpleasant-Profession-Jonathan-Hoag/dp/0441854575">The Unpleasant Profession of Jonathan
Hoag</a>
(1942), Robert Heinlein (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Cat-Who-Walks-through-Walls/dp/0441094996">The Cat Who Walks Through
Walls</a>
(1985), Robert Heinlein (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Integral-Trees-Larry-Niven/dp/B00266CDOC">The Integral
Trees</a>
(1984), Larry Niven (Pat Bottino)</li>
<li><a href="http://www.amazon.com/Smoke-Ring-Larry-Niven/dp/B001NDROG6">The Smoke
Ring</a>
(1987), Larry Niven (Pat Bottino)</li>
<li><a href="http://www.amazon.com/Inferno-Henri-Barbusse/dp/1617200875">Inferno</a>
(1976), Larry Niven and Jerry Pournelle (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Escape-Hell-Larry-Niven/dp/B0030EG174">Escape from
Hell</a>
(2009), Larry Niven and Jerry Pournelle (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Ringworlds-Children-Larry-Niven/dp/0765341026">Ringworld’s
Children</a>
(2004), Larry Niven (?)</li>
<li><a href="http://www.amazon.com/Draco-Tavern-Larry-Niven/dp/0765347717">The Draco
Tavern</a>
(2006), Larry Niven (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Radio-Free-Albemuth-Philip-Dick/dp/0679781374">Radio Free
Albemuth</a>
(1976), Philip K. Dick (Tom Weiner)</li>
<li><a href="http://www.amazon.com/Borderlands-Science-Where-Sense-Nonsense/dp/0195157982">The Borderlands of Science: Where Sense Meets
Nonsense</a>
(2001), Michael Shermer (Grover Gardener)</li>
<li><a href="http://www.amazon.com/People-Believe-Weird-Things-Pseudoscience/dp/0805070893">Why People Believe Weird Things: Pseudoscience, Superstition, and
Other Confusions of Our
Time</a>
(2002), Michael Shermer (Michael Shermer)</li>
<li><a href="http://www.amazon.com/Science-Good-Evil-People-Gossip/dp/0805077693">The Science of Good and Evil: Why People Cheat, Gossip, Care,
Share, and Follow the Golden
Rule</a>
(2004), Michael Shermer (Michael Shermer)</li>
<li><a href="http://www.amazon.com/Pale-Blue-Dot-Vision-Future/dp/0345376595">Pale Blue Dot: A Vision of the Human Future in
Space</a>
(1994), Carl Sagan (Carl Sagan)</li>
<li><a href="http://www.amazon.com/Demon-Haunted-World-Science-Candle-Dark/dp/0345409469">The Demon-Haunted World: Science as a Candle in the
Dark</a>
(1995), Carl Sagan (Michael Page)</li>
<li><a href="http://www.amazon.com/JPod-Novel-Douglas-Coupland/dp/1596911050">JPod</a>
(2006), Douglas Coupland (Marc Cashman)</li>
</ul>
<p>While all of the books were good, the quality of the audiobook depends almost entirely on the voice of the person reading it. So far my favorites are <a href="http://www.audiofilemagazine.com/gvpages/A2427.shtml">Tom Weiner’s</a> rich baritone and <a href="http://en.wikipedia.org/wiki/Richard_Dawkins">Richard Dawkins’</a> pleasant english accent. By far the worst, though, is anything read by Carl Sagan: I can highly recommend Carl’s plodding pace and odd word emphasis to insomniacs who don’t respond to strong drugs.</p>code_monkey_steveFor over a year I’ve been commuting for almost 2 hours a day, and in an attempt to stave-off potentially lethal boredom, I’ve been passing the time listening to audiobooks. Here’s what I’ve been reading/listening to:Toward a More Perfect Mongo ODM2011-04-24T00:00:00-07:002011-04-24T00:00:00-07:00https://finagle.org/2011/04/toward-a-more-perfect-mongo-odm<h2 id="mongodb-mongomapper-and-mongoid">MongoDB, MongoMapper, and Mongoid</h2>
<p>I’m now well into my third Rails project using the <a href="http://mongodb.org">MongoDB</a> document database, and while I’m still a big fan of Mongo, I’ve been underwhelmed by the ODM (Object-Document Mapper)s I’ve used. On the first project, I started with <a href="http://mongomapper.com">MongoMapper</a>, which is very mature and well-supported, but was a little klunky and tried a little too hard to be like <a href="http://ar.rubyonrails.org">ActiveRecord</a>.</p>
<p>For the second project, I switched to <a href="http://mongoid.org">Mongoid</a>, which was a huge improvement. It played nicely with ActiveSupport and ActiveModel, and had better support for doing things the Mongo way. But in the end, it had several nasty bugs related to associations and embedded documents, and the better I understood what I wanted from an ODM, the more I realized that Mongoid wasn’t it.</p>
<h2 id="the-alternatives">The Alternatives</h2>
<h5 id="candy"><a href="https://github.com/SFEley/candy">Candy</a></h5>
<p>I looked into Candy, and found its approach intriguingly fresh. Models don’t have to specify field names or types, and can be Arrays or Hashes or any other Ruby type. But I don’t like the lack of control over the serialization process (e.g. <code class="language-plaintext highlighter-rouge">find</code>, <code class="language-plaintext highlighter-rouge">save</code>, callbacks, validations, etc.), nor the absence of any sort of relational mechanism. Like Mongoid, it does have a nice query Criteria DSL, though.</p>
<h5 id="mongomatic"><a href="http://mongomatic.com">Mongomatic</a></h5>
<p>I’m not sure Mongomatic even qualifies as an ODM, as it doesn’t seem to do any mapping. From what I’ve seen, it’s just a thin wrapper around the Ruby MongoDB driver, adding little. I don’t know why anyone would bother using it.</p>
<h5 id="mongoodm-my-fork"><a href="https://github.com/carlosparamio/mongo_odm">MongoODM</a> (<a href="https://github.com/CodeMonkeySteve/mongo_odm/">my fork</a>)</h5>
<p>It could use a better name, but it’s a nice ODM, if a bit immature. I especially like its support for embedded documents, i.e. you don’t have to do anything special, just assign a variable of the specified Mongo-serializeable type (Document or otherwise) to a field, and it Just Works. It also supports Arrays and Hashes that can take any heterogeneous collection of types.</p>
<p>It’s also better designed under the hood than Mongoid or MongoMapper, taking full advantage of Ruby conventions to be easily hackable. MongoODM is definitely the best candidate for Perfect ODM I’ve yet seen.</p>
<h2 id="the-perfect-odm">The Perfect ODM</h2>
<p>Here’s what I really in my perfect Mongo ODM:</p>
<h5 id="plays-well-with-rails">Plays Well with Rails</h5>
<p>Like it or not, the ActiveRecord API is the standard convention for performing DB operations. And to the extent that SQL and MongoDB are conceptually similar, they should maintain the same API. This makes it easier to integrate with other software that may assume AR conventions, but more importantly, it keeps me from having to learn and remember a whole new set of only-slightly-different APIs.</p>
<h5 id="duck-typing-and-other-ruby-isms"><a href="http://en.wikipedia.org/wiki/Duck_typing">Duck typing</a> and Other Ruby-isms</h5>
<p>This is one big feature that ActiveRecord does not (and cannot) have, but which Mongo gives us almost for free — dynamic typing, just like native Ruby. Mongoid supports this for polymorphism, but MongoODM also supports dynamic types in Hashes and Arrays, and it was this fact that original attracted me to it. I have no problem with declaring document fields, but why should I have to specify the type? For that matter, why should I be constrained to a static type?</p>
<h5 id="schema-dsl">Schema DSL</h5>
<p>Even though I want the freedom to store any value of any type in any field, I know that schemas are still important, both for validation and configuration management. All ODMs provide ActiveRecord-style type-specifiers and validations (Mongoid and MongoODM also use ActiveModel), but I’d like to see document schemata become a top-level object, some superset of <a href="http://json-schema.org">JSON Schema</a>, with a friendly and extensible DSL. Something like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Person
schema do
property(:name) {
type String
length 1..20
required
}
property(:phone) {
type Phone
optional
}
property(:aliases) {
type Array.of(String)
optional
}
property(:vehicles) {
type Array.of(Car, Boat, Spaceship)
required
default []
}
additional_properties false
end
end
</code></pre></div></div>
<p>Once the schema is nestled into object form, there’s a whole bunch of interesting things you can do, in addition to validations:</p>
<ul>
<li>Schema versioning and heterogeneous collections</li>
<li>Data migrations and schema management</li>
<li>Client-side validations (via JSON Schema)</li>
<li>Automatic form generation (think: <a href="http://activescaffold.com">ActiveScaffold</a> on steroids)</li>
</ul>
<h5 id="references-and-associations">References and Associations</h5>
<p>This area gets a bit tricky, partially because of SQL’s wretched legacy of foriegn keys and join tables, but also because the problem is just inherently difficult. Ideally, the database or ODM would provide an equivalent to Ruby’s garbage-collected memory management system, where any document field could be a reference to any other object of any type, and all objects would be automatically destroyed when no longer used.</p>
<p>MongoDB actually comes pretty close with their support for <a href="http://www.mongodb.org/display/DOCS/Database+References">Database References</a>. These allow you to assign to a document field a reference to any document in any collection. I’ve expanded MongoODM with a transparent Reference proxy, and assigning a reference to a field is as simple as calling <code class="language-plaintext highlighter-rouge">.reference</code> (or <code class="language-plaintext highlighter-rouge">.ref</code>) on a document. I’ve been looking at adding something similar for <a href="http://www.mongodb.org/display/DOCS/GridFS+Specification">GridFS attachments</a>, but with a reference count to allow easy sharing of large binary objects between documents.</p>
<h2 id="the-future--no-odm">The Future — No ODM?</h2>
<p>This post is mostly just a dump of ideas I’ve had while working to expand MongoODM. But I find myself working more and more in Javascript, these days, and taking advantage of things like <a href="http://jquery.com">jQuery</a> and <a href="http://documentcloud.github.com/backbone">Backbone</a> to build rich client applications in the browser. In this situation, which I think will become more common, I don’t need so much ODM support in Ruby/Rails, and more-so in Javascript. So now I’m contemplating a MongoDB interface in JavaScript, passing through some sort of <a href="http://rack.rubyforge.org">Rack</a> proxy to perform access control. I’ll let you know how it turns out …</p>code_monkey_steveMongoDB, MongoMapper, and MongoidBack from the Dead2011-03-20T00:00:00-07:002011-03-20T00:00:00-07:00https://finagle.org/2011/03/back-from-the-dead<p>Greetings, everyone! Welcome to my new-and-improved blog.</p>
<p>I’ve decided to merge my coding blog (“One-Banana Problem”), which has gone woefully under-updated, with my website (“finagle.org”), which is gone woefully under-used, and you’re now looking at the result. If you’re thinking it looks a lot like One-Banana Problem …</p>
<ol>
<li>You actually read my blog? Wow thanks, you’re awesome, that almost doubles my audience!</li>
<li>It looks the same because it’s a port of the original content and style from OBP to a new platform.</li>
</ol>
<p>Why a new platform? I’m glad you asked, therein lies the story …</p>
<h5 id="the-story">The Story</h5>
<p>It all started a few months back, when I went to compose a long-overdue OBP post, and discovered that Mephisto wasn’t letting me login. How rude. A quick Google not only failed to provide a solution to the problem, but indicated that Mephisto was no longer maintained or supported. The consensus appeared to be to migrate to a combination of <a href="https://github.com/mojombo/jekyll">Jekyll</a>, a static-site generator, and the <a href="http://disqus.com/">Disqus</a> commenting system. Over the months since, I’ve followed the same path, deploying the new blog on <a href="http://heroku.com">Heroku</a>, my preferred hosting service. Porting the content was fairly easy, but porting the theme not so much.</p>
<h5 id="jekyll">Jekyll</h5>
<p>The best I can say about Jekyll is that it works well enough to get things running again. At first, I was considering using it for some static brochure sites for friends and family, but after getting it working I’ve decided it’s not good for me for a few reasons:</p>
<ul>
<li>No <a href="http://haml-lang.com">HAML</a> support. Although there are lots of hacks and extensions that claim to fix this, I could never get any of them to work.</li>
<li>Heroku provides Varnish to automatically cache pages, so generating static HTML content doesn’t give any significant speed improvement.</li>
<li>I do have to check-in all that static content to Git, which just needlessly bloats the Heroku slug.</li>
<li>Heroku provides a Rack interface, which means I still need to use a (tiny) <a href="http://sinatrarb.com">Sinatra</a> app to serve that static content.</li>
</ul>
<p>While I can appreciate the idea of storing the content in the blog code itself, with metadata in a YAML header block and the copy in <a href="http://en.wikipedia.org/wiki/Textile_(markup_language)">Textile</a>, I think I’ll stick to Sinatra for my static sites. I’m already working on a CMS-ish project that will probably replace Jekyll as soon as it’s done enough.</p>
<h5 id="moving-forward">Moving Forward</h5>
<p>I originally started One-Banana Problem in 2006, after getting laid-off from a failing startup. I wanted to make the switch from a C<span class="underline"></span> developer to a Ruby/Rails guy, and I thought a blog would help not only to share my hard-won knowledge, but also to market myself to the Rails community in hopes of finding a good job. To that end, I tried to keep the tone professional and the content mostly related to Ruby and Rails.</p>
<p>But here we are, five years later, and now I’m living living the dream of a full-time Ruby developer (woo-hoo!). However, having achieved that goal, I find myself with less motivation for blogging, and more importantly, with less time.</p>
<p>To counter this trend I’ve decided that, as part of The Great Blog Renaming, that I would expand the scope from a mostly coding-centric blog, to including hacking of all kinds. I am a geek, after all, and to my brain all problems have technological solutions. I have some interesting ideas for things like secure voting, digital currency, and even improvements to the democratic process (which hasn’t changed significantly in centuries), and I’ll try to find the time to comment on these sorts of political and social issues more freely in the future.</p>
<p>I’ve also come to the conclusion that trying to keep things “professional” is boring and pointless. If you can’t be open an honest bastard with the whole Internet, viewable by all and archive forever, then why bother blogging?</p>Steve SloanGreetings, everyone! Welcome to my new-and-improved blog.Take my money, please!2010-10-12T00:00:00-07:002010-10-12T00:00:00-07:00https://finagle.org/2010/10/take-my-money-please<p>One of the downsides to being a good web developer is that it makes it really hard to ignore bad web developers. Ever since I started building commercial websites with an emphasis on usability, I can’t help but notice when other sites get it completely wrong. Case in point: credit cards. Just about every commercial site accepts credit cards, and since that is arguably the only feature that a for-profit site really needs, I would expect a little more thought would be put into it. But instead, my online shopping experience is often stymied by developers who are apparently either too lazy or too stupid to make it easy for me to <em>give them my money</em>. Here are a few of the more annoying mis-features I’ve run across:</p>
<h3 id="card-number">Card number</h3>
<ul>
<li><strong>Wrong</strong>: “enter card number without spaces or punctuation”</li>
<li><strong>Right</strong>: strip invalid characters</li>
</ul>
<p>Any time you ask a customer to sacrifice their convenience for the sake of your app, things have gone horribly wrong. But especially in this case, where stripping unwanted characters is such a trivial matter, I can’t understand why any self-respecting developer would make such a blunder. Every modern web language - including Ruby, Javascript, and even PHP - supports regular expression substitution. All you have to do is:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">number</span><span class="p">.</span><span class="nf">gsub!</span><span class="p">(</span> <span class="sr">/[^\d]/</span><span class="p">,</span> <span class="s2">""</span> <span class="p">)</span>
</code></pre></div></div>
<h3 id="card-expiration-date">Card expiration date</h3>
<ul>
<li><strong>Wrong</strong>: “January”, “February”, “March” etc.</li>
<li><strong>Right</strong>: 01, 02, 03, etc.</li>
</ul>
<p>This is another no-brainer: the expiration date printed on my card is <code class="language-plaintext highlighter-rouge">03/14</code> (happy Pi month!), but some sites only give a list of months <em>by name</em>, forcing me to do the conversion in my head. Admittedly, the math is trivial, but why make customers do more work then they have to, especially when they are trying to <em>give you their money</em>. It’s no more difficult to populate a <code class="language-plaintext highlighter-rouge">select</code> element with numbers instead of words (in some ways, it’s actually easier), and any decent web framework should provide a function to do this for you; use it! In Rails, it’s just:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">select_month</span><span class="p">(</span> <span class="o">...</span><span class="p">,</span> <span class="ss">:add_month_numbers</span> <span class="o">=></span> <span class="kp">true</span> <span class="p">)</span>
</code></pre></div></div>
<h3 id="card-type">Card type</h3>
<ul>
<li><strong>Wrong</strong>: “select card type”</li>
<li><strong>Right</strong>: determine card type from card number</li>
</ul>
<p>This one involves slightly more effort, but since we’re trying to make it as easy as possible for people to <em>give us their money</em>, it’s totally worth it. The crucial piece of information here is the fact that the type of the credit card can be determined from the card number itself, using the same ubiquitous regular expressions we used above. Here’s a mapping of card numbers to (some of the) card types:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="sr">/^4/</span><span class="p">:</span> <span class="s2">"Visa"</span><span class="p">,</span>
<span class="sr">/^5[1-5]/</span><span class="p">:</span> <span class="s2">"Mastercard"</span><span class="p">,</span>
<span class="sr">/^3[47]/</span><span class="p">:</span> <span class="s2">"American Express"</span><span class="p">,</span>
<span class="sr">/^6011/</span><span class="p">:</span> <span class="s2">"Discover"</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="tldr">TL;DR</h3>
<p>Put a little extra effort into designing the payment section of your site, you want to make it as easy as possible for your customers to <em>give you their money</em>.</p>Steve SloanOne of the downsides to being a good web developer is that it makes it really hard to ignore bad web developers. Ever since I started building commercial websites with an emphasis on usability, I can’t help but notice when other sites get it completely wrong. Case in point: credit cards. Just about every commercial site accepts credit cards, and since that is arguably the only feature that a for-profit site really needs, I would expect a little more thought would be put into it. But instead, my online shopping experience is often stymied by developers who are apparently either too lazy or too stupid to make it easy for me to give them my money. Here are a few of the more annoying mis-features I’ve run across:Autotest and Ruby 1.9.22010-09-04T00:00:00-07:002010-09-04T00:00:00-07:00https://finagle.org/2010/09/autotest-and-ruby-1-9-2<p>There’s a <a href="http://rubyforge.org/tracker/index.php?func=detail&aid=28113&group_id=419&atid=1678">bug in autotest</a> that prevents it from properly finding a project’s autotest discover file under Ruby 1.9.2. Until an appropriate fix is made, you can work around it on the command line like so:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span> <span class="no">RUBYLIB</span><span class="o">=</span><span class="s1">'.'</span> <span class="n">bundle</span> <span class="nb">exec</span> <span class="n">autotest</span>
</code></pre></div></div>Steve SloanThere’s a bug in autotest that prevents it from properly finding a project’s autotest discover file under Ruby 1.9.2. Until an appropriate fix is made, you can work around it on the command line like so: