<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Dev Blog</title>
 <link href="https://chris.brooks6.com/" rel="self"/>
 <link href="https://chris.brooks6.com"/>
 <updated>2026-04-08T02:04:47+00:00</updated>
 <id>https://chris.brooks6.com</id>
 <author>
   <name>Christopher Brooks</name>
   <email>chris@brooks6.com</email>
 </author>

 
 <entry>
   <title>DuoLingo and Supernatural VR: Stay or Go?</title>
   <link href="https://chris.brooks6.com/2026/04/07/stay-or-go"/>
   <updated>2026-04-07T00:00:00+00:00</updated>
   <id>https://chris.brooks6.com/2026/04/07/stay-or-go</id>
   <content type="html">
&lt;p&gt;There are two popular applications that have had my goodwill, my attention and my subscriptions, that I’ve soured on in the last few months for different reasons.&lt;/p&gt;

&lt;p&gt;Supernatural, a VR exercise app, launched in April of 2020. It was founded by Chris Milk, who has music industry connections from his time directing music videos, which meant he was positioned to license popular music set to BeatSaber-style workouts, with human coaches providing (offline) direction. The launch timing was fortuitous, with everyone looking for ways to workout at home during the pandemic. It’s one of the most successful VR subscription apps, with plenty of daily users who swear by it. Facebook aka Meta was so excited about it, they spent a year and a half purchasing the company, including a legal battle with the FTC. I bought my Quest 3 mostly for Supernatural.&lt;/p&gt;

&lt;p&gt;Then in January, Meta announced “Supernatural will no longer receive new content or feature updates starting today.” For now, the old workouts are still available; it’s not known how long the licensing deals stay in place. So it’s a zombie app now.&lt;/p&gt;

&lt;p&gt;Fans like myself have to decide: are we ok with the existing list of workouts and nothing new coming out? Are we cool giving $10 a month to Meta for this? Zuckerburg told us all to follow the VR dream with him, and even renamed his company around it. Now AI is the new hotness, and VR is being unsentimentally dumped by Meta. (What will their new name be? Clanker?)&lt;/p&gt;

&lt;p&gt;For now I’m going to leave my Supernatural subscription in place so I can access the back-catalogue, but I feel like a sucker doing it. Consumer spending sends a message, and I hate sending Meta the message that what they did with Supernatural and their VR effort in general, is ok.&lt;/p&gt;

&lt;p&gt;Then we have Duolingo, the language learning app. Duolingo has been around fifteen years or so. They have tons of gamification to keep users engaged. I have a 1035 day streak of doing at least one lesson every day, and a 473 day friend streak with my wife. Honestly though, the streaks are kind of a sham. You can get “streak freezes” in case you forget a day. And I’m pretty sure I was out of streak freezes at least once, and Duolingo gave me a pass, anyway. They’ve also done the opposite: told me I was going to lose my streak if I didn’t do a lesson, when I knew I had streak freezes left. The friend streaks are more effective: if you do your lesson and your friend hasn’t, you can “nudge” them to get it done. I’m not even sure you CAN lose a friend streak, but the peer pressure to do a lesson is real.&lt;/p&gt;

&lt;p&gt;Anyway, in March, Duolingo extensively reorganized a bunch of their old Spanish lessons. As a side effect, the app no longer knows what I know and don’t know. It thinks I’ve completed lessons I haven’t, and so asks me questions about words or phrases I never learned. I tried to go back a few chapters to re-do them so I can get reoriented, but the app doesn’t have a way to say “this is where I am now, pretend I never did this chapter and remember I’m here now”. I spent a lot of time and effort getting to where I am in the content, and Duolingo seems to have just invalidated that progress without a second thought. Even though I know in my heart that five minutes a night isn’t going to make me a fluent Spanish speaker, that still feels bad. I’m going to stick with Duolingo for now, but my faith is a lot shakier now.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>One Year With Ruby: What I Like and Dislike</title>
   <link href="https://chris.brooks6.com/2014/11/26/one-year-with-ruby"/>
   <updated>2014-11-26T00:00:00+00:00</updated>
   <id>https://chris.brooks6.com/2014/11/26/one-year-with-ruby</id>
   <content type="html">
&lt;p&gt;I came from a long-time C++ background, working in games for many years.&lt;/p&gt;

&lt;p&gt;For nearly the last year, I’ve been working full-time building and enhancing a website implemented in Ruby on Rails. One of the major appeals of this project for me was the chance to use and build on what I’d learned doing side projects in RoR.&lt;/p&gt;

&lt;p&gt;Here are my thoughts about Ruby nearly one year in.&lt;/p&gt;

&lt;h1 id=&quot;what-i-like&quot;&gt;What I Like&lt;/h1&gt;

&lt;h2 id=&quot;dynamically-typed&quot;&gt;Dynamically Typed&lt;/h2&gt;

&lt;p&gt;Coming from C++, it was very liberating not having to specify the type of every variable. I overestimated the number of bugs that would result: for the most part, passing the wrong type ends up causing a clear exception pretty quickly, and is probably the easiest kind of bug to track down. And it happens much less frequently than I would have guessed. The tradeoff in terms of reduction in code size has been totally worth it.&lt;/p&gt;

&lt;p&gt;Having said that, the code only throws an exception if it gets run. I wonder if some of the emphasis on TDD in the Rails community is to make up for the lack of static type checking, by trying to ensure every code path is run regularly.&lt;/p&gt;

&lt;h2 id=&quot;very-expressive&quot;&gt;Very Expressive&lt;/h2&gt;

&lt;p&gt;You can do so much with so little code. But unlike perl, the code ends up readable. So readable in fact, that self-documenting code actually seems practical a lot more often.&lt;/p&gt;

&lt;p&gt;Having expressive code makes it so much easier to hold a complex problem in your head, when you can see all the related code on one page.&lt;/p&gt;

&lt;h2 id=&quot;blocks&quot;&gt;Blocks&lt;/h2&gt;

&lt;p&gt;Being able to pass a bit of code to functions isn’t new, but Ruby’s block syntax makes it so simple to do that you end up using it everywhere.&lt;/p&gt;

&lt;h2 id=&quot;symbols&quot;&gt;Symbols&lt;/h2&gt;

&lt;p&gt;They’re like C++ enums that you don’t have to declare ahead of time! I’m a big fan of symbols. Yes, you could use string constants instead, but symbols feel so much cleaner and more efficient.&lt;/p&gt;

&lt;h2 id=&quot;function-calls-without-parentheses&quot;&gt;Function Calls Without Parentheses&lt;/h2&gt;

&lt;p&gt;In many places, parentheses are optional when calling a function in Ruby.&lt;/p&gt;

&lt;p&gt;It makes the calling code so much cleaner:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  class Train
    def cars()
      [:engine, :coal, :caboose]
    end
  end
  
  puts train.cars  # So clean!
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;what-i-dislike&quot;&gt;What I Dislike&lt;/h1&gt;

&lt;h2 id=&quot;skipping-parentheses-with-function-calls-causes-ambiguity&quot;&gt;Skipping Parentheses With Function Calls Causes Ambiguity&lt;/h2&gt;

&lt;p&gt;From O’Reilly’s “The Ruby Programming Language”:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  puts(sum 2,2)  # Does this mean puts(sum(2,2)) or puts(sum(2),2)?
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Also, because skipping the parenthesis is possible, if you wish to use the parentheses you can’t leave any space between the function name and the start paren:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  sum(1,2) # ok

  sum (1,2) # bad
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I started off skipping the parens wherever I could, but I’ve come around to only skipping them in limited cases. They’re great for making:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  a_string.length
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;look like a property access, for example. Maybe not so great when you’re doing:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  my_object.do_something :now, 3, &apos;hello&apos;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And just confusing if you’re doing nested calls:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  puts sum 1,2  # Gross!
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;---block-or-hash&quot;&gt;{} - Block or Hash?&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;  def func(arg)
    puts arg
  end

  func {} # Ruby thinks I&apos;m passing a block; &quot;I get ArgumentError: wrong number of arguments (1 for 0)&quot;

  func({}) # This actually passes the hash in
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This seems like a minor nitpick, but it makes me feel like being able to skip parens, and {} being used for both blocks and hashes, leads to some unintuitive edge cases.&lt;/p&gt;

&lt;h2 id=&quot;attributes-vs-local-variables&quot;&gt;Attributes vs Local Variables&lt;/h2&gt;

&lt;p&gt;What’s bar do?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  class Foo
    attr_accessor :var

    def bar
      var = 5
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That’s right! It creates a local variable called “var” and throws it away!&lt;/p&gt;

&lt;p&gt;You have to do &lt;code&gt;self.var = 5&lt;/code&gt;. This is kinda crummy because most of the time, you don’t need the &lt;code&gt;self.&lt;/code&gt; But in this one case, because calling a member assignment function would look just like assigning to a new local variable, leaving out the &lt;code&gt;self.&lt;/code&gt; results in a silent failure that can be a pain to track down.&lt;/p&gt;

&lt;h2 id=&quot;unused-blocks-are-ignored&quot;&gt;Unused Blocks are Ignored&lt;/h2&gt;

&lt;p&gt;It’s easy to do this by mistake:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  MyModel.where(name: &apos;bob&apos;) { |m| puts m }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The block never gets called, because where() doesn’t use its block; you need to do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  MyModel.where(name: &apos;bob&apos;).each { |m| puts m }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It feels like that block passed in where it isn’t needed should result in an error, just like passing an argument to a function where it isn’t needed does. But because the &lt;code&gt;yield&lt;/code&gt; syntax for using a block doesn’t require anything in the function declaration line, Ruby doesn’t have an easy way to detect if the block is ever used.&lt;/p&gt;

&lt;h2 id=&quot;dsls-domain-specific-languages&quot;&gt;DSLs (Domain Specific Languages)&lt;/h2&gt;

&lt;p&gt;Ruby makes DSLs practical to create, and powerful. As long as you’re using the DSL more or less as its creator intended, everything’s great, and you get to deal with things at a higher level of abstraction.&lt;/p&gt;

&lt;p&gt;The problem can come when, as a user of the DSL, you try to DRY up your code, because you have the same set of steps with tiny differences happening in multiple places. You may end up having to learn how the DSL is actually implemented, which busts the abstraction wide open.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;I’ve had a great time learning and using Ruby. It’s a wonderful language, and I use it everywhere I used to use Python. Yes, there’s some quirks, but the positives far outweigh the negatives.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Side project: u8this.com</title>
   <link href="https://chris.brooks6.com/2014/02/19/side-project-u8thiscom"/>
   <updated>2014-02-19T00:00:00+00:00</updated>
   <id>https://chris.brooks6.com/2014/02/19/side-project-u8thiscom</id>
   <content type="html">
&lt;p&gt;Over the last month (first checkin Jan. 23), I’ve been working on a side project. It’s a food diary site, for calorie-counting. I’m working on losing weight, and wanted to do something more complicated in rails than my &lt;a href=&quot;/2013/12/08/timing/&quot;&gt;last project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find the site here: &lt;a href=&quot;http://u8this.com&quot;&gt;u8this.com&lt;/a&gt; Feel free to make an account and mess around with it. (Put garbage data in if you want to see how it works; I won’t be offended.)&lt;/p&gt;

&lt;p&gt;The code is here: &lt;a href=&quot;https://github.com/csbrooks6/u8this&quot;&gt;github.com/csbrooks6/u8this&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve tried to use all the latest tools in building it:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Rails 4.0&lt;/li&gt;
  &lt;li&gt;RSpec&lt;/li&gt;
  &lt;li&gt;Developing on osx, using Sublime Text 3&lt;/li&gt;
  &lt;li&gt;Deploying to linode using Capistrano&lt;/li&gt;
  &lt;li&gt;postgres&lt;/li&gt;
  &lt;li&gt;Trello to track my TODO list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to document some of the different challenges I ran into, and techniques I used.&lt;/p&gt;

&lt;h2 id=&quot;idea&quot;&gt;Idea&lt;/h2&gt;

&lt;p&gt;The idea, in my mind anyway, isn’t revolutionary. At least in terms of getting users, chances are low I’m going to be able to peel users away from the myriad other food diary sites. And even if I could, I think monetizing is unlikely.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;border:3px solid #808080&quot; src=&quot;/assets/images/u8this_food.jpg&quot; /&gt;
&lt;em&gt;&lt;a href=&quot;https://www.flickr.com/photos/epsos/&quot;&gt;Photo by epSos.de&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There’s upsides to this idea, though:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Solidify my Rails knowledge&lt;/li&gt;
  &lt;li&gt;Use some different things I haven’t used before, like RSpec&lt;/li&gt;
  &lt;li&gt;It’s something I personally want to use, so I can make it work exactly the way I like&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;git-locally-linode-and-github&quot;&gt;Git: Locally, linode and github.&lt;/h2&gt;

&lt;p&gt;From the start I used git for source control. First a local repository, then I created a remote on my linode server, and now Github. I haven’t yet switched completely to Github, so I’m still pushing all changes both to my linode and github at the same time.&lt;/p&gt;

&lt;p&gt;I mostly use SourceTree for osx, which I really like. Clean interface, very flexible. My only complaint is that I’ve been getting dramatic slowdowns at times. Other than that, it’s awesome.&lt;/p&gt;

&lt;p&gt;I’ve mostly been working straight out of the master branch, though as the site has gone online, I’ve made a dev branch and switched over to that, so I could do a quick bugfix in master and push it live without waiting for my dev changes to be complete.&lt;/p&gt;

&lt;h2 id=&quot;rspec-and-tdd&quot;&gt;RSpec, and TDD&lt;/h2&gt;

&lt;p&gt;This is my first real foray into test-driven development. I found a good e-book called &lt;a href=&quot;https://leanpub.com/everydayrailsrspec&quot;&gt;“Everyday Rails Testing with RSpec”&lt;/a&gt; that I used to help me figure out what I was doing.&lt;/p&gt;

&lt;p&gt;I have a number of RSpec tests now. I haven’t yet built any integration tests, just model and controller specs. I also don’t really have 100% coverage. I guess I’m still not fully in the mode of writing the tests first and the code second. I need to focus on that more, because it’s worked well when I try it. (Although one disadantage is that if you are still learning RSpec, it’s easy to write tests that aren’t correct. Then inevitably you end up rewriting the tests anyway as the code comes online. It’s getting easier, though.)&lt;/p&gt;

&lt;p&gt;All in all, I’ve really enjoyed RSpec.&lt;/p&gt;

&lt;h2 id=&quot;authentication&quot;&gt;Authentication&lt;/h2&gt;

&lt;p&gt;I did some research, and settled on &lt;a href=&quot;https://github.com/binarylogic/authlogic&quot;&gt;Authlogic&lt;/a&gt; for account authentication. Seemed full-featured and well-supported, and there was even a decent example project on their github site that I used as a reference point.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;border:3px solid #808080&quot; src=&quot;/assets/images/he_chose_poorly.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I probably should’ve been warned away when I figured out the sample was written for Rails 2. It took some finagling to get it running in Rails 4. I emailed the author and offered to submit a pull request to update the sample to Rails 4, but no reply. (Another warning?)&lt;/p&gt;

&lt;p&gt;The bottom line is, sadly, Authlogic doesn’t seem to be in active development. I get a bunch of rails deprecation warnings when I run my RSpec tests against it, that I think are fixed in a pull request, but that either hasn’t been integrated or something.&lt;/p&gt;

&lt;p&gt;The warnings look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DEPRECATION WARNING: ActiveRecord::Base#with_scope and #with_exclusive_scope 
are deprecated. Please use ActiveRecord::Relation#scoping instead. (You can
use #merge to merge multiple scopes together.)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Super annoying to get dozens of those when I rspec.&lt;/p&gt;

&lt;p&gt;Anyway, now I’ve figured out everyone is using &lt;a href=&quot;https://github.com/plataformatec/devise&quot;&gt;Devise&lt;/a&gt;. It’s on my TODO list to switch everything over.&lt;/p&gt;

&lt;h2 id=&quot;bootstrap&quot;&gt;Bootstrap&lt;/h2&gt;

&lt;p&gt;I decided to use Bootstrap, to implement a responsive design. Working well on both smartphone browsers and PCs was important to me. Bootstrap also makes things look a lot slicker than I would have had time to do.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;border:3px solid #808080&quot; src=&quot;/assets/images/u8this_bootstrap.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I used Bootstrap 3, but it hasn’t been out terribly long, which means a lot of gems and stackoverflow questions are targetting Bootstrap 2.3 instead.&lt;/p&gt;

&lt;p&gt;I used a gem called &lt;a href=&quot;https://github.com/twbs/bootstrap-sass&quot;&gt;bootstrap-sass&lt;/a&gt;. In retrospect, I think it would have been simpler just to directly integrate the css and js downloaded directly from Bootstrap. There’s a particular issue I’m running into with Bootstrap that I think is fixed in the latest version. So I’m planning to try updating the bootstrap-sass gem, and if that breaks, I’ll look at getting rid of it and just putting Bootstrap into the asset pipeline myself.&lt;/p&gt;

&lt;p&gt;The form controls look nice, though it took plenty of fiddling to get them to work right. I also lost all the clean Rails helper functions for views to emit form controls. There are gems like &lt;a href=&quot;https://github.com/plataformatec/simple_form&quot;&gt;simple_form&lt;/a&gt; that can help with that, but at the moment, it targets Bootstrap 2.3.&lt;/p&gt;

&lt;h2 id=&quot;typeaheadjs-and-bloodhound&quot;&gt;Typeahead.js and Bloodhound&lt;/h2&gt;

&lt;p&gt;I used Twitter’s &lt;a href=&quot;http://twitter.github.io/typeahead.js/&quot;&gt;typeahead.js&lt;/a&gt; library for entering foods, so ones you’d previously entered would autocomplete. (I intentionally chose not to autocomplete to stuff other users had entered, because God knows what they put in there.)&lt;/p&gt;

&lt;p&gt;Typeahead used to be part of bootstrap, but is now its own thing. I was a little disappointed with it, just because I had the idea it was going to have something cool behind the scenes to intelligently make queries with my dataset. It doesn’t do that though, and in the end my controller-side code is doing some horrible &lt;code&gt;LIKE&lt;/code&gt; sql query that I’m still feeling iffy about. (It’s surprisingly hard to handle all the edge cases in using user-submitted data with &lt;code&gt;LIKE&lt;/code&gt; through Rails, without letting bad data through. I couldn’t google up any clean database-independent way to do it.)&lt;/p&gt;

&lt;p&gt;Typeahead is all about just knowing when it needs to request data, and showing the results that come back, and letting the user autocomplete or arrow-key select them. Basically, just the user interface side.&lt;/p&gt;

&lt;p&gt;It can be used together with something called &lt;a href=&quot;https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md&quot;&gt;Bloodhound&lt;/a&gt;. Aha, so that’s the part that intelligently finds search results, right? Well, not quite. It’s job is to manage preloaded data, requesting more data, caching and rate-limiting those requests. But in the end you’ll still be making a request to your site with a string query, and it’s up to you to figure out what results match that string. &lt;code&gt;LIKE&lt;/code&gt; to the rescue!&lt;/p&gt;

&lt;p&gt;Typeahead also had some weird interactions with Bootstrap (surprising since they’re both from Twitter). It’s totally possible I’m doing something wrong, but the Bootstrap input text control text hints fight with the autocomplete hints that Typeahead shows. I ended up just turning off my Bootstrap hint text, for now.&lt;/p&gt;

&lt;p&gt;I also ran into something weird where calling the typeahead() function on my input form control made the rounded edges that Bootstrap puts on it, disappear. After some investigating I think I tracked this down to typeahead “wrapping” the form control element when that function is called, which makes the css for the rounded corners not work anymore.&lt;/p&gt;

&lt;p&gt;I &lt;a href=&quot;http://jsfiddle.net/csbrooks/m4LT3/&quot;&gt;tried to repro this in a jsfiddle&lt;/a&gt;, but it doesn’t seem to happen there. So I’m thinking it’s something with the version of Bootstrap I’m running, which is why I want to upgrade.&lt;/p&gt;

&lt;p&gt;Anyway, for now, after I call typeahead() I just do this: &lt;code&gt;$(&apos;.foods.typeahead&apos;).css(&apos;border-radius&apos;, &apos;4px&apos;);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Gross, but it works. Like Javascript! I kid, I kid.&lt;/p&gt;

&lt;h2 id=&quot;javascriptcoffeescript-ajax&quot;&gt;Javascript/Coffeescript, AJAX&lt;/h2&gt;

&lt;p&gt;I wanted to avoid having an ugly page reload when you modify or add “Servings” (my name for the ActiveRecord behind “something you ate”). So I used AJAX calls, using the “remote” method on my forms, and jquery/coffeescript listening for the AJAX to return. (Actually I didn’t use XML, I used JSON.)&lt;/p&gt;

&lt;p&gt;The add and update controller methods would put the html for the new or modified Serving in the JSON object. Other methods would refer to the id of the Serving to be deleted, or moved up or down, or whatever. Then the coffeescript would update the page as appropriate.&lt;/p&gt;

&lt;p&gt;One minor hitch was that I show a progress bar at the top of the page with the number of calories, and I wanted that to reflect any changes. So I took the most straightforward approach, and had the controllers just render the view partial for the progress bar and stuff that in each of the JSON return values, too.&lt;/p&gt;

&lt;p&gt;A more major hitch I ran into was that the AJAX seemed to stop responding when I would navigate to the next or previous page. This is simpler to describe now than it was to track down, but it turns out that turbolinks makes jquery’s $(document).ready not get called, which causes all sorts of madness. The answer is a gem called &lt;a href=&quot;https://github.com/kossnocorp/jquery.turbolinks&quot;&gt;jquery-turbolinks&lt;/a&gt;, which fixes this issue. (My first impulse was to turn off turbolinks, but the site did respond much slower. Although that was with &lt;code&gt;rails server&lt;/code&gt; in development on my macbook, so probably not a great test, but still.)&lt;/p&gt;

&lt;p&gt;One thing I wanted to do was show a context menu when you click a Serving on the page. Bootstrap doesn’t directly support this, but I found some code online that did the trick, mostly. I had to convert it to Coffeescript from Javascript, and make various tweaks for my needs. I also threw in some of the “glyphs” that Bootstrap comes with. I’m pretty happy with the results:&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;border:3px solid #808080&quot; src=&quot;/assets/images/u8this_context_menu.jpg&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;capistrano&quot;&gt;Capistrano&lt;/h2&gt;

&lt;p&gt;I heard this was the way to go for deployment. It really wasn’t too difficult to setup, though I got stuck for a while getting the ssh-agent forwarding to work. (That’s so that my private key on my macbook can essentially be used to issue commands to my linode server.)&lt;/p&gt;

&lt;p&gt;I actually even got a staging server running while I was at it, so I could have an environment to test my deployment to linode before pushing it live.&lt;/p&gt;

&lt;p&gt;Now every time I do “cap production deploy” and watch it do its thing, I pretty much feel like a badass. I’ve even been known to shout out &lt;em&gt;“I am become death, the deployer of worlds!”&lt;/em&gt;, which no one thinks is funny but me.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Basic Web Scraping in Ruby with Mechanize</title>
   <link href="https://chris.brooks6.com/2014/01/05/basic-web-scraping-ruby"/>
   <updated>2014-01-05T00:00:00+00:00</updated>
   <id>https://chris.brooks6.com/2014/01/05/basic-web-scraping-ruby</id>
   <content type="html">
&lt;p&gt;As I mentioned &lt;a href=&quot;/2013/08/14/side-project-wowtcg-pricelist&quot;&gt;previously&lt;/a&gt;, as a side project, I created a site to collect average prices for World of Warcraft Trading Card Game cards. Here’s how I wrote the code in Ruby to gather the data I needed from various card store websites.&lt;/p&gt;

&lt;h1 id=&quot;mechanize&quot;&gt;Mechanize&lt;/h1&gt;

&lt;p&gt;I use &lt;a href=&quot;http://mechanize.rubyforge.org/&quot;&gt;Mechanize&lt;/a&gt; to interact with the sites I need data from: to visit URLs, pull down the HTML, and even submit forms. Mechanize fills the role of a headless browser, though it doesn’t do javascript.&lt;/p&gt;

&lt;p&gt;Here’s code to print out google’s home page:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  require &apos;mechanize&apos;

  # Create mechanize instance
  mechanize = Mechanize.new

  # Get the page
  page = mechanize.get(&apos;http://google.com&apos;) 
  puts page.body
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;Mechanize::get&lt;/code&gt; call returns us a &lt;a href=&quot;http://mechanize.rubyforge.org/Mechanize/Page.html&quot;&gt;&lt;code&gt;Mechanize::Page&lt;/code&gt;&lt;/a&gt; instance. We can use this to do some basic html parsing, and to find links and follow them, or to find forms and fill them out and submit them. But primarily, I use &lt;a href=&quot;http://nokogiri.org/&quot;&gt;Nokogiri&lt;/a&gt; through the Page object, to search HTML using XPath.&lt;/p&gt;

&lt;h1 id=&quot;using-xpath&quot;&gt;Using XPath&lt;/h1&gt;

&lt;p&gt;XPath is a query language used to find nodes in XML, and therefore, HTML. (&lt;a href=&quot;http://nokogiri.org/tutorials/ensuring_well_formed_markup.html&quot;&gt;according to the docs&lt;/a&gt;, Nokogiri cleans up badly formed HTML as best it can, and my experience so far has been that it does just fine.)&lt;/p&gt;

&lt;p&gt;The trickiest part is figuring out what XPath query to use. I use a Chrome extension called “&lt;a href=&quot;https://chrome.google.com/webstore/detail/xpath-helper/hgimnogjllphhhkhlmebbmlgjoejdpjl&quot;&gt;XPath Helper&lt;/a&gt;” for this. You can click an element to get the corresponding XPath, and then play with the XPath query to see how to generalize it to pickup similar elements on that page.&lt;/p&gt;

&lt;h2 id=&quot;finding-card-categories&quot;&gt;Finding Card Categories&lt;/h2&gt;

&lt;p&gt;For example, say I wanted to get links to all the categories of cards on a page like this:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;http://www.mtgmintcard.com/wow_category.php&quot;&gt;http://www.mtgmintcard.com/wow_category.php&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Chrome, I’d hit ctrl-shift-X to turn on XPath Helper. Then I’d hold down shift, and roll over one of the category names (let’s try “Betrayal of the Guardian”), which would turn yellow. While still rolled over it, I release shift, and now I can edit the query to generalize it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/images/mtgmint_categories.jpg&quot;&gt;
&lt;img style=&quot;border:3px solid #808080&quot; src=&quot;/assets/images/thumbnail_mtgmint_categories.jpg&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the XPath query starts out like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/html/body[@id=&apos;wowcategoryBody&apos;]/div[@id=&apos;mainWrapper&apos;]/table/tbody/tr/td[1]/div[@class=&apos;tabBody&apos;]/div[@id=&apos;normal_search&apos;]/div[@id=&apos;allEdition&apos;]/table/tbody/tr/td[1]/span[@class=&apos;symbol&apos;]/a[1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: As the XPath Helper extension mentions, the &lt;code&gt;&amp;lt;tbody&amp;gt;&lt;/code&gt; tag inside tables isn’t really in the html coming down from the site; Chrome adds it artificially.&lt;/em&gt; So we need to make sure to remove the &lt;code&gt;&amp;lt;tbody&amp;gt;&lt;/code&gt; from the query before you give it to Nokogiri, or the query won’t find anything in Ruby. Delete the &lt;code&gt;&amp;lt;tbody&amp;gt;&lt;/code&gt; but leave the double &lt;code&gt;//&lt;/code&gt; so the query matches both in XQuery Helper and in Nokogiri:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/html/body[@id=&apos;wowcategoryBody&apos;]/div[@id=&apos;mainWrapper&apos;]/table//tr/td[1]/div[@class=&apos;tabBody&apos;]/div[@id=&apos;normal_search&apos;]/div[@id=&apos;allEdition&apos;]/table//tr/td[1]/span[@class=&apos;symbol&apos;]/a[1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we want to generalize this to find &lt;em&gt;all&lt;/em&gt; categories, not just the one we chose. Those &lt;code&gt;[1]&lt;/code&gt;’s are what’s selecting only the &lt;em&gt;first&lt;/em&gt; column, or &lt;em&gt;first&lt;/em&gt; anchor. So we can experiment in the query removing those, and watch as the yellow highlight updates in Chrome.&lt;/p&gt;

&lt;p&gt;We end up with this query:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/html/body[@id=&apos;wowcategoryBody&apos;]/div[@id=&apos;mainWrapper&apos;]/table//tr/td[1]/div[@class=&apos;tabBody&apos;]/div[@id=&apos;normal_search&apos;]/div[@id=&apos;allEdition&apos;]/table//tr/td/span[@class=&apos;symbol&apos;]/a
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note in the screenshot that the XPath Helper “Results” listbox shows the names of all the categories that have been matched by the query.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/images/mtgmint_categories_2.jpg&quot;&gt;
&lt;img style=&quot;border:3px solid #808080&quot; src=&quot;/assets/images/thumbnail_mtgmint_categories_2.jpg&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I prefer to simplify the query, to try to make it both cleaner and hopefully more resilient to future site changes. In XPath queries, a &lt;code&gt;//&lt;/code&gt; will match anything, so we can delete the first part of the query and simplify it down to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//div[@id=&apos;allEdition&apos;]/table//tr/td/span[@class=&apos;symbol&apos;]/a
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a matter of keeping enough of the query to match only what we want, while removing anything that’s extraneous. Yes, this is subjective.&lt;/p&gt;

&lt;p&gt;Now if we turn this into Ruby code to use Mechanize and Nokogiri to pick out the categories, it looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  require &apos;mechanize&apos;
  require &apos;nokogiri&apos;

  # Create Mechanize instance
  mechanize = Mechanize.new

  # Retrieve the categories page
  front_page = mechanize.get(&apos;http://www.mtgmintcard.com/wow_category.php&apos;)

  # Find the category links
  category_links = front_page.search &quot;//div[@id=&apos;allEdition&apos;]/table//tr/td/span[@class=&apos;symbol&apos;]/a&quot;
  category_links.each do |cat_link|
    # NOTE: actual link is in cat_link[&quot;href&quot;]
    puts cat_link.text
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When run, the output will look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Betrayal of the Guardian
War of the Ancients
Tomb of the Forgotten
Crown of the Heavens
Throne of the Tides
Twilight of the Dragons
War of the Elements
Worldbreaker
Icecrown
Archives
... &amp;lt;snip&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;drilling-down-to-find-products&quot;&gt;Drilling down to find products&lt;/h2&gt;

&lt;p&gt;Let’s modify the loop to actually click through the link for each category, and retrieve the result:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  category_links.each do |cat_link|
    # Create a Page::Link and follow it.
    result_page = Mechanize::Page::Link.new( cat_link, mechanize, front_page ).click
    puts result_page.at(&apos;//title&apos;).text # Print the page title
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The ugly line to create the link and click it is necessary because we aren’t using the &lt;code&gt;Mechanize::Page&lt;/code&gt;’s ability to find links (see &lt;code&gt;Mechanize::Page.links&lt;/code&gt; and &lt;code&gt;Mechanize::Page::link_with&lt;/code&gt;), but are getting the link through Nokogiri instead. (I prefer to run everything through Nokogiri/XPath instead of using the Mechanize::Page method, but you don’t have to.)&lt;/p&gt;

&lt;p&gt;Ok, let’s look at one of these product pages, and figure out how to get product data out of it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;http://www.mtgmintcard.com/world-of-warcraft/english-regular/betrayal-of-the-guardian&quot;&gt;http://www.mtgmintcard.com/world-of-warcraft/english-regular/betrayal-of-the-guardian&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This page has a table with a bunch of rows, one product per row. Let’s figure out an XPath query to get all the rows in that table.&lt;/p&gt;

&lt;p&gt;First, using XPath Helper, select let’s select the column with a product name in it. The query looks something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/html/body[@id=&apos;wowsearchresultBody&apos;]/div[@id=&apos;mainWrapper&apos;]/table/tbody/tr/td[1]/div[@id=&apos;indexDefault&apos;]/table[@class=&apos;resultList&apos;]/tbody/tr[@class=&apos;darkRow&apos;][1]/td[2]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first thing I notice is that every other row has a &lt;code&gt;class=&apos;darkRow&apos;&lt;/code&gt;, but we want ALL the rows, not just every one. Let’s clip off everything after the last tr. I’ll cut out the &lt;code&gt;tbody&lt;/code&gt;s, too:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/html/body[@id=&apos;wowsearchresultBody&apos;]/div[@id=&apos;mainWrapper&apos;]/table//tr/td[1]/div[@id=&apos;indexDefault&apos;]/table[@class=&apos;resultList&apos;]//tr
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That query picks up the top row that has column names as well, but that’s ok, we’ll worry about that later.&lt;/p&gt;

&lt;p&gt;Looking at the query, we really want all the rows in the table with a class of &lt;code&gt;resultList&lt;/code&gt;. So let’s simplify it to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//table[@class=&apos;resultList&apos;]//tr
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again, you can verify this query works in XPath Helper.&lt;/p&gt;

&lt;p&gt;Ok, let’s get paths to individual product names and prices. By experimenting, we can figure out that this selects all the names (which are in the second column, hence the &lt;code&gt;[2]&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//td[2]/a[@class=&apos;opacityit&apos;]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This gets us all the prices:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//td[8]/span[@class=&apos;productListPrice&apos;]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If we’re not sure those will always be in the second and eighth columns, we could leave the &lt;code&gt;[2]&lt;/code&gt; and &lt;code&gt;[8]&lt;/code&gt; out of the queries.&lt;/p&gt;

&lt;p&gt;Ok, now we’re to going to put it together, to iterate over all the rows in the table, and find the product name and price for each row, if there is one:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  # Iterate over all the rows in the resultList table
  result_page.search(&quot;//table[@class=&apos;resultList&apos;]//tr&quot;).each do |row|
    # Name
    name_node = row.at(&quot;.//td[2]/a[@class=&apos;opacityit&apos;]&quot;)
    unless name_node 
      next
    end

    # Price
    price_node = row.at(&quot;.//td[8]/span[@class=&apos;productListPrice&apos;]&quot;)
    unless price_node
      next
    end

    puts &quot;%s: %s&quot; % [name_node.text, price_node.text]
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that &lt;code&gt;.&lt;/code&gt; at the start of the &lt;code&gt;at&lt;/code&gt; queries we’re doing on each row. If we left that off, the XPath query would actually go back to the top of the DOM, instead of only searching inside the row node.&lt;/p&gt;

&lt;p&gt;Ok, so now we’re getting the name and price of cards on the first page in a category. We need to be able to click the “Next” link on each page to get &lt;em&gt;all&lt;/em&gt; the results:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  next_page_link = result_page.at &quot;//a[text()=&apos;Next&apos;]&quot;
  if next_page_link
    result_page = Mechanize::Page::Link.new( next_page_link, mechanize, result_page ).click
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That query &lt;code&gt;//a[text()=&apos;Next&apos;&lt;/code&gt; means ‘find all the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags where the text is “Next”’.&lt;/p&gt;

&lt;p&gt;Alright, let’s put it all together. This will iterate over all the categories, drill down into each one, and pull out name and price data for all the products in that category:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  require &apos;mechanize&apos;
  require &apos;nokogiri&apos;

  # Create Mechanize instance
  mechanize = Mechanize.new

  # Retrieve the categories page
  front_page = mechanize.get(&apos;http://www.mtgmintcard.com/wow_category.php&apos;)

  # Find the category links
  category_links = front_page.search &quot;//div[@id=&apos;allEdition&apos;]/table//tr/td/span[@class=&apos;symbol&apos;]/a&quot;
  category_links.each do |cat_link|
    # Create a Page::Link and follow it.
    result_page = Mechanize::Page::Link.new( cat_link, mechanize, front_page ).click

    while result_page do
      puts result_page.at(&apos;//title&apos;).text # Print the page title

      # Iterate over all the rows in the resultList table
      result_page.search(&quot;//table[@class=&apos;resultList&apos;]//tr&quot;).each do |row|
        # Name
        name_node = row.at(&quot;.//td[2]/a[@class=&apos;opacityit&apos;]&quot;)
        unless name_node
          next
        end

        # Price
        price_node = row.at(&quot;.//td[8]/span[@class=&apos;productListPrice&apos;]&quot;)
        unless price_node
          next
        end

        puts &quot;%s: %s&quot; % [name_node.text, price_node.text]
      end

      next_page_link = result_page.at &quot;//a[text()=&apos;Next&apos;]&quot;
      if next_page_link
        result_page = Mechanize::Page::Link.new( next_page_link, mechanize, result_page ).click
      else
        result_page = nil
      end
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output will look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Betrayal of the Guardian - World Of Warcraft TCG (Cryptozoic)
A Demonic Presence: $0.29
Aegwynn, Guardian of Tirisfal: $0.24
Ancient Moonkin Form: $0.24
Arcane Anomaly: $0.24
Arcane Protector: $0.24
Arcane Shock: $0.24
Archdruid Fandral Staghelm: $2.24
Assault on Blackrock Spire: $0.24
Atiesh, Greatstaff of the Guardian: $0.24
Bastion of Defense: $0.24
Belthira the Black Thorn: $8.49
Bianca, Timewalker Mage: $0.24
Bigbelly, Furbolg Chieftain: $0.24
Bitey: $0.24
Blackfang Tarantula: $0.29
... &amp;lt;snip&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That’s the basics of how I pull out product name and price information from different stores in my WoWTCG card pricelist site. I plan to make another post going up one level, and documenting what I do with this information once I have it.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>'Carmack Lines': Playing with Javascript</title>
   <link href="https://chris.brooks6.com/2014/01/04/carmack-lines-playing-with-javascript"/>
   <updated>2014-01-04T00:00:00+00:00</updated>
   <id>https://chris.brooks6.com/2014/01/04/carmack-lines-playing-with-javascript</id>
   <content type="html">
&lt;p&gt;I read &lt;a href=&quot;https://twitter.com/ID_AA_Carmack/status/418976175533223936&quot;&gt;this tweet&lt;/a&gt; by John Carmack:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Visualize an image where a pixel is on if a given bit is set in the sum of the x and y coordinates. I had poor intuition for its character.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I too had “poor intuition for its character”, but I’m pretty sure that’s not because I’m as smart as he is or anything. But still, I was curious how it would look.&lt;/p&gt;

&lt;p&gt;I’ve also been looking for an excuse to play with Javascript and html5 canvases, and I couldn’t sleep at 5:30 this morning, so I popped up and had at it.&lt;/p&gt;

&lt;p&gt;It took me less than an hour to get this running, even though I haven’t done anything significant in Javascript in years. And only maybe 20 minutes of that was the basic javascript, the rest was getting the form working right to let you dynamically change which bit is tested.&lt;/p&gt;

&lt;p&gt;I think that’s more a testament to the power of &lt;a href=&quot;http://stackoverflow.com/&quot;&gt;stackoverflow&lt;/a&gt; than anything else. Maybe ten times while implementing this I found the answer there, where before, it would have taken a lot more reading and experimentation. It’s impressive that a single site with user generated content has had such an impact on the programming process. (At a meetup I went to, another programmer said to me “I can’t imagine programming before stackoverflow!” I wanted to say “imagine what it was like before we had the internet”, but I try to repress my inner old fogey when possible.)&lt;/p&gt;

&lt;p&gt;I’m kind of amazed at how fast Javascript executes, at least in Chrome. I’m iterating over 16,384 pixels every time you hit the button, and there’s no perceptable delay at all, at least on my Macbook. Makes me want to do some more experiments with graphics in javascript.&lt;/p&gt;

&lt;p&gt;Anyway, &lt;a href=&quot;/static_html/bit_image.html&quot;&gt;here it is&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Timing</title>
   <link href="https://chris.brooks6.com/2013/12/08/timing"/>
   <updated>2013-12-08T00:00:00+00:00</updated>
   <id>https://chris.brooks6.com/2013/12/08/timing</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;“Ask me what the most important thing in comedy is.”&lt;/p&gt;

  &lt;p&gt;“What’s the most important thi-“&lt;/p&gt;

  &lt;p&gt;“Timing.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Where my last post left off, I was getting ready to rewrite my pricelist website in Ruby on Rails. I decided to do this so I could get some solid hands-on experience with Ruby and with Rails. And the python version had gotten crusty enough that a rewrite was looking enticing.&lt;/p&gt;

&lt;p&gt;The rewrite took my spare time for several weeks, and I ended up using all sorts of fun stuff:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Passenger&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://mechanize.rubyforge.org/&quot;&gt;Mechanize&lt;/a&gt; to parse html – much nicer than BeautifulSoup, once you get used to it&lt;/li&gt;
  &lt;li&gt;rvm to manage gems and other deployment crud&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/choice&quot;&gt;choice&lt;/a&gt;, to write a command-line script to gather and calculate the card price data&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linode.com/&quot;&gt;linode&lt;/a&gt; for hosting – a lot better than my old host&lt;/li&gt;
  &lt;li&gt;The &lt;a href=&quot;https://github.com/flyerhzm/bullet&quot;&gt;bullet&lt;/a&gt; gem, to help optimize rails queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, I got everything up and running, and working better than ever! I made an announcement on the wowtcg forums, and opened the revamped site for “business”. While that was live, I created a new branch to work on moving to bootstrap, to be more mobile-friendly. I also signed up for an ebay developer account, and wrote some scripts to gather price data there.&lt;/p&gt;

&lt;p&gt;Then a couple of weeks later, Cryptozoic and Blizzard announced they were terminating the World of Warcraft Trading Card Game, after a run of almost seven years.&lt;/p&gt;

&lt;p&gt;So, that sucked.&lt;/p&gt;

&lt;p&gt;Anyway, now I need to figure out a new side project. I started working on making a game engine using SDL2 and box2d, but I’ve decided that isn’t the direction I want to go in right now.&lt;/p&gt;

&lt;p&gt;I’m looking for a project that combines some (not all!) of these aspects:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ruby on Rails&lt;/li&gt;
  &lt;li&gt;Using large public datasets&lt;/li&gt;
  &lt;li&gt;ebay api&lt;/li&gt;
  &lt;li&gt;Geocoding/mapping (integrating with google maps, maybe)&lt;/li&gt;
  &lt;li&gt;Very usable on mobile – bootstrap?&lt;/li&gt;
  &lt;li&gt;The potential to make money, even if it’s just a little. Not so much because I need the money, but for the experience of building something that generates income.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Side Project: WoWTCG Pricelist</title>
   <link href="https://chris.brooks6.com/2013/08/14/side-project-wowtcg-pricelist"/>
   <updated>2013-08-14T00:00:00+00:00</updated>
   <id>https://chris.brooks6.com/2013/08/14/side-project-wowtcg-pricelist</id>
   <content type="html">
&lt;p&gt;I got totally hooked on the World of Warcraft Trading Card Game, from the time it came out six or seven years ago. (It’s like Magic: The Gathering, but without mana screw or mana flood. And the players tend to smell less bad. Plus it’s the Warcraft universe!)&lt;/p&gt;

&lt;p&gt;I went from being a casual player who didn’t really understand the rules, to spending hours looking over cards online and trying to work out decks. Then I started going to local weekly tournaments and connecting with local players, and then things really spiralled out of control. I ended up driving to regional tournaments that were a couple hours away, probably once a month or so. Eventualy I ended up going to nationals at GenCon a couple times. (I didn’t do terrible, but I didn’t do amazingly well either: finished about middle of the field.)&lt;/p&gt;

&lt;p&gt;If you aren’t familiar with trading card games, you open “booster packs” that have a bunch of random cards. Some cards are a lot more rare, and useful, and therefore valuable than others. There are stores online that will sell “singles”, so rather than buying random boosters you can just pay for the cards you want. These cards vary in price from $100 at the very extreme end, but most are 25 cents, or a dollar, or maybe five or ten dollars.&lt;/p&gt;

&lt;p&gt;What I found was that sometimes it was difficult to work out on-the-spot if a given card is worth a dollar, or five, or ten, which made trading tricky. So I wrote a script in Python that would scrape five or six different online stores for prices on singles, and build up an html pricelist with averages every night, so at least people would have a rough idea what a given card is worth.&lt;/p&gt;

&lt;p&gt;The website is at &lt;a href=&quot;hexagog.com&quot;&gt;hexagog.com&lt;/a&gt;, and I posted it under my forum name of “Conrad Hex”, which I use so no one can find out my true identity. Crap, I guess that’s blown.&lt;/p&gt;

&lt;p&gt;Anyway, the site worked well for a long time. I loved it, other people loved it. I got emails of appreciation, and even got some free products from one of the developers of the game. But about a year ago it fell into disrepair. The site I used to update my card list database had gone away, and I wasn’t really active in the game itself anymore. Everyday I would get a sad little cron job email with a python exception, because one of the sites had changed their page layout, and the scraper couldn’t do its job any more.&lt;/p&gt;

&lt;p&gt;Things stayed that way until I decided about a month ago to bring it back to life, this time in Ruby on Rails.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To be continued…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That’s me on the right with the red shirt, geekin’ out hard:&lt;/strong&gt;&lt;br /&gt;
&lt;img src=&quot;/assets/images/chris_at_realm_champs.jpg&quot; alt=&quot;Chris playing wowtcg&quot; /&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>First Post!</title>
   <link href="https://chris.brooks6.com/2013/08/13/first-post"/>
   <updated>2013-08-13T00:00:00+00:00</updated>
   <id>https://chris.brooks6.com/2013/08/13/first-post</id>
   <content type="html">
&lt;p&gt;I was motivated to start writing after reading Matt Swanson’s article “&lt;a href=&quot;http://mdswanson.com/blog/2013/08/11/write-things-tell-people.html&quot;&gt;Do things, write about it&lt;/a&gt;” on hacker news yesterday. I posted a comment on HN asking what’s a good way to put up a development blog, and Mr. Swanson and others recommended jekyll and github pages, so here we are.&lt;/p&gt;

&lt;p&gt;I’ve had a blog before, but I was terrible at updating it. I think that’s because I was being too much of a perfectionist: I spent about a month polishing my “article” before posting it. (I did get a bunch of hits from it, but man was it painful to produce.)&lt;/p&gt;

&lt;p&gt;I’m working on a side project, and I wanted to document how things are going, and also have a place to post tips or fixes as I find them. For example, I got a nasty issue with my site going down because I changed my .bash_profile to exec screen on startup, and somehow that took down Passenger on my production site. It would have been perfect to post the error message here, in case someone else gets the same issue they could google the fix.&lt;/p&gt;

&lt;p&gt;Anyway, my goal is to post here pretty regularly. I’m not going to try to target the quality level of a book or anything this time, but maybe more the quality level I would use for email to a group of ten other programmers at work about something important. I think that will be a good balance so I can post stuff I’m not ashamed of, and still actually get stuff posted without endless noodling.&lt;/p&gt;
</content>
 </entry>
 
 
</feed>