Doing It All!
I'm not really in the habit of putting non-code content on this blog, but more than one person asked me the same question at RubyConf. If people really want to know, I'll try to answer. Paraphrasing, the question was:
How do you keep up with so many Ruby projects?
First, this question surprised me. Do I really do that much? If you just said yes to that, I would like to introduce you to Ryan Davis. He easily doubles my output and his projects are wicked complex compared to mine.
That doesn't answer the question though.
In short, I do as much as I possibly can with the time I have. The truth is that I would like to do a lot more. I turn down at least as many damn cool Ruby projects as I accept because I'm a wimp and not willing to give up my sleep. There are so many crazy cool Ruby projects out there that I would love to be a part of. There just aren't enough hours in the day.
I guess I still didn't answer the question.
The question was "How…" and the answer to that is actually trivial. Masayoshi Takahashi summed it up with a single slide in his presentation at RubyConf:
I Just Want One Character!
Every so often a person asks the question on Ruby Talk, "How can I get just one character from the keyboard (without needing the user to hit return)?" Everyone is always quick to post solutions, but sadly there are some issues with almost every one of them.
The general consensus is that this is a tough problem to solve correctly. I say that's the exact reason to let HighLine handle this for you:
#!/usr/bin/env ruby -w require "highline/system_extensions" include HighLine::SystemExtensions print "Enter one character: " char = get_character puts char.chr
That doesn't look too tough, does it?
What's terrific about this solution is that under-the-hood
HighLinewill check your platform and libraries and then try to use the solution that makes the most sense for your environment. The code is really pretty robust too, because people a lot smarter than me have been sending in patches for over a year, slowly eliminating all of those tricky edge cases.
As you can see, I've split this functionality of
HighLineinto a separate module so you don't even need to load the full
HighLinesystem. This was done just because this is such a real and common problem. This section of
HighLineis one pure Ruby file, so feel free to vendor it if the external dependency is an issue.
Just recently I have been working with two different people to improve their regular expression skills. To help me in this endeavor, I built a trivial little script we have been using in IRb. To get started, you construct a new challenge object and add a couple of challenges:
>> reg_chal = RegexpChallenge.new No challenges. => >> reg_chal.challenge("Gray, James", "James", "The names can vary.") => nil >> reg_chal.challenge("abbbbbbbc bc", 10) => nil >> reg_chal.challenge( " \n\t ", nil, ?> "We want to test for non-space data." ) => nil >> reg_chal.challenge( "cogs 9, widgets 12, ...", "12", ?> "The numbers can vary." ) => nil >> reg_chal.challenge( "I'm a simple sentence, with words.", ?> %w[I'm a simple sentence with words] ) => nil
You can ask for challenges to see what you would like to solve:
>> reg_chal.challenges Challenge #0: Input: "Gray, James" Output: "James" Note: "The names can vary." Challenge #1: Input: "abbbbbbbc bc" Output: 10 Challenge #2: Input: " \n\t " Output: nil Note: "We want to test for non-space data." Challenge #3: Input: "cogs 9, widgets 12, ..." Output: "12" Note: "The numbers can vary." Challenge #4: Input: "I'm a simple sentence, with words." Output: ["I'm", "a", "simple", "sentence", "with", "words"] => nil
PStore Meets YAML
I love the
PStorestandard library. It's a very graceful interface to get some fairly robust serialized mini-database handling in just a few lines. With it you get:
- Transactions with commit and rollbacks (automatic on exception).
- File locking, shared and exclusive.
- Multiprocessing safety.
PStoredoes even more, including some file mode checking and MD5 hashing to avoid unneeded writes, but the above are the major selling points for me.
Now, if I had to level any one complaint at
PStore, it would be that because it uses
Marshalunder the hood it doesn't create files you can easily browse or tweak by hand. (
Marshalis a feature, don't get me wrong. It's fast, which is very helpful.) Sometimes though I want
PStoreprotection with the
I'm embarrassed to admit that I use to use a hack for this:
require "pstore" require "yaml" class PStore; Marshal = YAML; end
That just redefines the
Marshalconstant in a scope that should only alter
PStore. The library only uses
load()and those methods work the same with
YARV Looking Promising, James's C is Not
I participated in the ICFP programming contest last weekend with a group of friends. We had a great time with the event and learned a ton. I thought I should share two interesting insights with others that might appreciate them.
First, YARV looks very promising for some general speed increases in Ruby. If you are not familiar with YARV, that's the virtual machine that will run Ruby 1.9. During the contest, we ran into some performance issues with our Ruby solution and after we had optimized all we could think of, we decided to try running our entry on the experimental YARV VM to see if it was faster there. Good news: it was a lot faster.
Please do not take these numbers as anything more than very non-scientific observations, but we did notice a huge speed increase on YARV. We were reliably waiting around 15 minutes for one section of our program to run on Ruby 1.8.4, but when we introduced YARV the same section generally ran in just under seven minutes. You heard me right there, it was over twice as fast. I think that's very promising news for the future of Ruby.
String Has Other Methods Besides =~/match() and sub()
Ask anyone who knows me and they will tell you I'm a huge fan of regular expressions. I use them all the time and my
FasterCSVlibrary is a regular expression powered parser. However, even I know they are not for everything, and lately I keep running into almost comical examples of misuse. Here are some of my favorites.
First, we have:
str =~ /=/
That snippet is like calling for a military escort (the regular expression engine) to see you safely to the grocery store down the block. That's fun, but probably overkill. In this case, a call to
include?()will do the trick:
That may be more like riding your bike to the grocery store, but it gets the job done and is a bit faster to boot.
Funny example number two. I've seen this before:
str =~ /\Aquit\Z/
Again, the regular expression engine appreciates the love, but you really just want
str == "quit"
Even for some of the fancier stuff, you don't need a full blown regular expression. For example, this:
The Books are Wrong About Logger
I've read several books that introduced the standard
Loggerlibrary and they all agree on one thing: you can't customize the output. That's so last version in thinking! Behold…
Here's a trivial
Loggerscript, showing basic functionality:
#!/usr/bin/env ruby -w require "logger" def expensive_error_report sleep 3 # Heavy Computation Simulation (patent pending) "YOU BROKE IT!" end log = Logger.new(STDOUT) log.level = Logger::INFO # set out output level above the DEBUG default log.debug("We're not in the verbose debug mode.") log.info("We do see informative logs though.") if log.error? # check that this will be printed, before waste time log.error(expensive_error_report) end
If you run that you will see:
I, [2006-07-08T11:17:19.531943 #340] INFO -- : We do see informative logs though. E, [2006-07-08T11:17:22.532424 #340] ERROR -- : YOU BROKE IT!
Now everyone has always known you can format the date and time display using a
#!/usr/bin/env ruby -w require "logger" def expensive_error_report sleep 3 "YOU BROKE IT!" end log = Logger.new(STDOUT) log.level = Logger::INFO log.datetime_format = "%Y-%m-%d %H:%M " # simplify time output log.debug("We're not in the verbose debug mode.") log.info("We do see informative logs though.") if log.error? log.error(expensive_error_report) end
Do I Need (These Parentheses()?)
If you came to Ruby via the Learn to Program book or just don't yet have a consistent set of rules for when you do and don't need parentheses in Ruby code, this post is for you.
I have nothing against Learn to Program, just to be clear. A member of my family is learning Ruby from it and it's going pretty well. I recommend it. However, Chris is a little inconsistent with his use of parentheses in the code samples, and worse, he doesn't really give you a good set of rules to decide when to make the choice. No problem. Let me give you the rules.
I'm a chess player. In learning chess, you really go through two phases. First, you learn the rules of strategy. These will make you good because the rules are designed to help you avoid common mistakes. Now, to get great, you go through the second phase: learning when to break the strategy rules. Ruby is exactly the same.
Here's the only rule of strategy you need to learn to get good: methods need parentheses around their arguments.
Unit Testers Get More Chicks
Just recently, a developer I respect very much was caught uttering the this surprising statement: "Unit tests just really aren't my thing." Now, I still respect this developer very much and I can tell you that the person single-handedly created one of my very favorite pieces of software. However, I do think the developer is dead wrong on this point. This is my attempt to change the mind of everyone that thinks similar things about unit testing.
My belief is that unit testing is for everyone and, in fact, I'll go so far as to say that I believe becoming a test-driven developer is the single best change a programmer can make in their day to day routine. Here are just some of the reasons why:
- Though counterintuitive, I swear that it makes you code faster. No one tends to believe me on this, but most test-driven developers come to this realization eventually. The reason is simple: you spend much less time debugging the 935 errors from your two hour code sprees.
- The tests are my memory. My head is too full of all the languages, frameworks, APIs, and family birthdays I am expected to know to remember everything I've ever done on top of that. This week at work I've touched three separate projects all with over 1,000 lines of code. I'm sure I had good ideas when I wrote those lines, but now I doubt I can tell you what they are. My tests can though. They remember so I don't have to. If I go into the code and change something I don't remember was needed or why, my tests will remind me immediately.
- In team development, my test powered memory even travels to the machines of the other developers! How cool is that? When someone goes into the code and says, "Why on Earth did James do this? We don't need this. I'm going to change that…" my tests will look after my interests for me.
- I'm a lot more confident in my software. I use to say things like, "I just finished this, so it hasn't been used much yet and probably still has plenty of issues." Now by the time I finish something my tests have been using the heck out of it. I won't kid you and tell you that my software now springs fully formed from the head of my tests, but it definitely comes out farther along the track.
- Environment changes seldom surprise me anymore. When I move my software to a different box and it can't cope for whatever reason, I not only know the first time I run the tests, I have an excellent idea of exactly where the problem is.
Ruby Idioms Bundle for TextMate
[Update: An improved version of Ruby Idioms has been merged into the Ruby bundle in TextMate. Everyone gets Ruby Idioms with TextMate now.]
If you are a TextMate user (a Mac OS X text editor), we have something in common and you might be interested in the rest of this post. If not, skip it.
I have made my Ruby Idioms bundle available for anyone who would like to download it and try it out. This bundle contains all the Ruby shortcuts I use to program everyday. I've spent a lot of energy over the last six months refining them down to the keystroke, so hopefully there is a lot of value hidden in there.
If you just want to get to the goods and start playing, click the above link and run. However, if you want some insight into my thinking with these shortcuts, read on…
I'm a Unix guy, so I'm very comfortable with two or three letter triggers and that's what you are going to find here. Sometimes I use a one or two caracter trigger (r, w, and rw just make sense for
attr_accessor, for example), but three is the norm.