-
30
MAY
2014Objectified Beer
I got another thing out of my recent conversation with Katrina Owen: a will-not-let-go itch to try a programming exercise that she mentioned. I'm such a sucker for a challenge.
Katrina spoke of an assignment that her and Sandi Metz have used in their object orientation trainings. She said that they build an OO implementation of the 99 Bottles of Beer song, "mainly removing
if
s and such." There may be more to the actual task than this, but I ran with that brief explanation.As I often say, you'll probably learn more by trying the exercise for yourself before you read through my thoughts about it. Give it a go if you can spare the time.
Diff Driven Development
I decided to throw some scaffolding into the master branch of a Git repository. I figured I could then branch off of that with each idea, to keep trying things out.
I started by constructing a trivial framework for running and verifying the song. That consisted of an executable:
#!/usr/bin/env ruby -w require_relative "../lib/bottles_of_beer" verses = ARGV.first =~ /\A\d+\z/ ? ARGV.shift.to_i : 99 BottlesOfBeer::Song.new(verses).sing($stdout)
-
21
MAY
2012Decorators Verses the Mix-in
It is a neat time to be involved in the Ruby community, if you ask me. A large portion of us are currently studying the techniques for doing good object oriented development. We are looking at the ideas that have come before and trying to decide the best ways to apply those ideas to our favorite language. This leads to blog posts, forum threads, and conference talks about what we are learning. No matter what, we all gain from explorations like this. Everybody wins as our collective knowledge grows. We all deserve gold stars.
So far, there's one point pretty much everyone agrees on: composition should typically be preferred to inheritance. The trickier part of that discussion though is deciding what composition looks like in Ruby. Generally you see Rubyists comparing the merits of decorators and mix-ins. [Note: the comments correctly pointed out that this was a bad use of the word "composition" on my part, to describe mix-ins.] There's a very representative thread on the excellent Objects on Rails mailing list.
-
1
MAY
2012Single Method Classes
[Update: I've changed my mind about some of the following due to this excellent counter argument.]
In the words of Dennis Miller, "I don't want to get off on a rant here, but…"
There's something that drives me crazy and I see it in so much Ruby code. I see it in the documentation for our key projects; I see Rubyists of all skill levels doing it; it's just everywhere.
Let's talk about when the use of a
Class
is and is not appropriate.The Chained new()
Here's an example of one form of code that bugs me:
class Adder def add(n) 40 + n end end p Adder.new.add(2)
The problem here is that a
Class
has been used, probably because as Rubyists that's always our default choice, but it's the wrong fit for this code. AClass
is for state and behavior. The example above is just using behavior. No state is maintained.A handy tip for sniffing out this problem is watching for a call to
new()
in the middle of method chaining as we have here. If you always use aClass
like that, it's not really aClass
. Put another way, if an instance never gets assigned to a variable, something has likely gone wrong with the design. -
21
MAR
2012Learn to Love Mix-ins
The road to mastering Ruby is paved with understanding some key Ruby concepts. Mix-ins are one of those concepts. I'm sure everyone reading this knows the mechanics of how mix-ins work, but it pays to spend some time really thinking about all that mix-ins imply. Let's do just that.
Adding a Type
One of the primary reasons that Ruby needs mix-ins is that it does not support multiple inheritance. That leaves mix-ins as our only option for modeling hybrid objects. It's the way Ruby programmers can add another type.
That's a good way to think about it too: adding a type.
Take pagination, for example. Pagination methods are usually defined to return an object like this:
class PaginatedCollection < Array # ... paginated helpers defined here ... end
That's never really felt right to me though.
First, inheriting from Ruby's core classes can come back to bite you in some scenarios. The reason is that Ruby makes some performance tradeoffs to keep the core classes fast, but those tradeoffs mean that those classes don't always perfectly follow Ruby's rules.
-
1
MAR
2012The Right Ruby Mix
Ruby is a melting pot language. It borrows ideas from many things that came before. It combines several different programming philosophies.
This aspect of the language can be a plus. It means that Ruby is suited to multiple applications. It also opens up some pragmatic shortcuts. Even better, it sometimes encourages us to think about problems using a different lens of thought.
Of course, this cuts both ways. Ruby living at the intersection of many ideas does have some downsides. First, there's more to learn than you find with some simpler languages. There's a cost for the extra knowledge we have to track. Even worse though, in my opinion, is that it's sometimes hard to know exactly what Ruby's style really is.
Going Off Script
One culture Ruby borrowed heavily from is that of the so called "Scripting Languages." The main source of these features was Perl, in my opinion, but you can also find influences from Bash and other sources. I found this comforting since I came to Ruby from Perl, but the truth is that it bothers some people.
-
20
FEB
2006Infinite Streams
I've tried to summarize this chapter a couple of times now, but I keep getting tripped up over syntax. So, let's talk about that…
Functional Perl
Obviously, the examples in the book can be more or less directly translated. Here's a sample from the first couple of pages:
#!/usr/local/bin/ruby -w ### Stream Methods ### def node(head, tail) [head, tail] end def head(stream) stream.first end def tail(stream) tail = stream.last if tail.is_a?(Proc) tail.call else tail end end def drop(stream) head = head(stream) stream[0], stream[1] = Array(tail(stream)) head end def show(stream, limit = nil) while head(stream) && (limit.nil? or (limit -= 1) > -1) print drop(stream), $, || " " end print $/ end ### Examples ### def upto(from, to) return if from > to node(from, lambda { upto(from + 1, to) }) end show(upto(3, 6)) # => 3 4 5 6 def upfrom(start) node(start, lambda { upfrom(start + 1) }) end show(upfrom(7), 10) # => 7 8 9 10 11 12 13 14 15 16
-
4
JAN
2006Pathname and Enumerator
I'm always digging around in Ruby's standard library looking for new toys. It's one-stop-shopping for geeks. I love it.
There really are some great tools in there that can help you do your work easier. Let me tell you a little about two I am using on a current application.
Pathname
Pop Quiz: Which would you rather work with?
if File.exist? some_dir data = File.read( File.join( File.dirname(some_dir), "another_dir", File.basename(some_file) ) ) # ... end
Or:
if some_dir.exist? data = (some_dir.dirname + "another_dir" + some_file.basename).read # ... end
If you prefer the second version, the
Pathname
library is for you.Ruby's file manipulations are usually fine for normal work, but anytime I start messing with directories I really start feeling the pain. For that kind of work, I find
Pathname
to be a superior interface. My current project makes great use of the methods in the above example as well asPathname#entries
,Pathname#relative_path_from
, and more.