Tuesday, December 8, 2009

Managing dotfiles with git

I've never managed dotfiles before, but now that I'm a serious vim user I thought it was time to get on with it. Naturally, I googled around to see what other fellows had done. I found a few different schemes. They all seemed more complicated than they should be. Some had scripts to run and symlinks everywhere. Others appeared to require a lot of manual bit pushing to maintain.

all of this because dotfiles are in your home directory along with a lot of other stuff you want to ignore. There is a very simple way to take care of this, let's get started.

First, you'll need a git repository. So cd into your home directory and type
git init

Now run the following command
git status

you should see a gigantic list of files. Some we want to version with git, most we do not.

Now here's the first trick. We want git to ignore everything by default, so we type:
echo "*" > .gitignore

Now run 'git status' again and git will tell you there's nothing to add to the repository. Excellent!

At this point you have a choice. You can manually add the dotfiles you want to version by prefixing their name with a '!' like this:
echo "!.vim" >> .gitignore

This makes for a short .gitignore file, but git won't tell you when new dotfiles are added - they'll be globally ignored along with everything else. That may be fine for some, but I like to know when new dot files are created.

To do this, I told git not to ignore anything that was prefixed with a '.'. My .gitignore file now looks like this:
/*
!.*

Now when you run 'git status' you get a big list of all the dotfiles that have not been versioned, most of these we want to ignore. So we capture the output in a file:
git status > unversioned

Open 'unversioned' and '.gitignore' in your favorite editor, copy the file list from 'unversioned' to the bottom of '.gitignore'. Now remove everything that you *DO* want to version. Be careful not to ignore your '.gitignore' file! That'd be silly.

Finally, add and commit your changes normally:
git commit -a -m "hey dotfiles! woohoo!"

That's it! If you want to store your dotfiles on github create a repo and follow their instructions for setting up github with an existing git repository.

You can see my dotfiles repo here

Cheers!

Thursday, October 29, 2009

A response to "Failures in Isolation"

Following Corey Haines on twitter is such a good idea. Today he dropped this gem:

Please read, I'm not going to paraphrase it. Nevermind, I will. But it will be really short. Poorly designed code that is tested with a high level of isolation causes a problem. You can't refactor it with confidence because the tests, or the mocks, are blocking your progress. So let's talk about how they impede refactoring first.

Tests prevent behavior from flowing 'up'. For instance, if you mistakenly coupled a model class to a view tier class you could not 'refactor' this problem without breaking the tests for your model. Most likely, the test will fail because the API of the class has changed, you no longer pass the request.

For Mocks it is the opposite. You cannot move behavior 'down'. Let's say I have leaked some model behavior into my controller and I wish to move it to the appropriate model. If my test uses the real model, not a mock, this is not a problem. I can move the behavior and my controller test will still pass. Now I can safely write model tests for the new behavior and delete the controller test that now duplicates it.

If I have mocked the model, however, my controller test will fail when I attempt to move the behavior 'down' into the model. One offense is not an issue. I was involved in one system where an entire test suite was thrown away because of improperly used mocks. Death by a thousand paper cuts.

I have no universal solution for this problem - as it is a problem of my skill as a designer and the skill of my peers. However, I have come up with two helpful guidelines.

Only directly test code you like

I dislike code when I'm unsure of its design. I also dislike code that appears volatile. As my confidence has grown I find myself more comfortable with my early design decisions and more willing to glorify my creations with their own test sooner. When I am not confident I keep my tests at a high enough level where I can refactor freely. As my confidence grows in a system I move towards higher levels of isolation.

Only mock code you like

For all the same reasons as above. Mocks crystalize an API for a class and can cause a lot of problems if the class is used often in a system and the class it is poorly designed. As the ugliness and volatility of code increase, mock it less.

In Conclusion

As with everything, the value of isolation in testing is not an absolute. For younger developers I suggest beginning with a state-based testing approach over using mocks pervasively. A state-based style will afford you maximum flexibility to refactor your design. As your experience grows begin to use mocks to solve some of the problems that state-based testing will invariably bring about.

That is the path I travelled and it worked out alright for me.

Friday, August 7, 2009

A log4j adapter for Rails

This current client application I'm working on is a mixed bag of Ruby and Java parts. All our existing logging for the older Java parts uses log4j and we wanted to unify our Rails logging with that. I spent a couple of hours putting together this adapter we now use. If you need something similar it should give you a good place to start.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class Log4jAdapter
include ActiveSupport::BufferedLogger::Severity
L4JLevel = org.apache.log4j.Level

SEVERETIES = {
DEBUG => L4JLevel::DEBUG,
INFO => L4JLevel::INFO,
WARN => L4JLevel::WARN,
ERROR => L4JLevel::ERROR,
FATAL => L4JLevel::FATAL,
}
INVERSE = SEVERETIES.invert

def initialize
@logger = org.apache.log4j.Logger.getLogger('com.shc.mmh.Rails')
@root = org.apache.log4j.Logger.getRootLogger
end

def add(severity, message = nil, progname = nil, &block)
message = (message || (block && block.call) || progname).to_s
@logger.log(SEVERETIES[severity], message)
end

def level
INVERSE[@logger.getEffectiveLevel]
end

def level=(level)
raise "Invalid log level" unless SEVERETIES[level.to_i]
@root.setLevel(SEVERETIES[level.to_i])
end

def enabled_for?(severity)
@logger.isEnabledFor(SEVERETIES[severity])
end

#Lifted from BufferedLogger
for severity in ActiveSupport::BufferedLogger::Severity.constants
class_eval <<-EOT, __FILE__, __LINE__
def
#{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
add(
#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
end # end
#
def
#{severity.downcase}? # def debug?
enabled_for?(
#{severity}) # DEBUG >= @level
end # end

EOT

end

def method_missing(meth, *args)
puts "UNSUPPORTED METHOD CALLED: #{meth}"
end
end

Wednesday, August 5, 2009

A Scala version of Unle Bob's lazy PI sequence in Clojure

I follow Uncle Bob on twitter and I'm also currently learning Scala. When I saw his tweet about a lazily evaluated infinite PI sequence in Clojure I couldn't resist giving it a shot in Scala. Here's my result (it works!). Feedback is absolutely welcome.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//Gives us the :: syntax for streams.  From http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient.
class RichStream[A](str: =>Stream[A]) {
def ::(hd: A) = Stream.cons(hd, str)
}
implicit def streamToRichStream[A](str: =>Stream[A]) = new RichStream(str)

object M { //Gives us a shorthand for declaring BigDecimals that Scala lacks
def apply(i:Int) = BigDecimal(i)
}

//Hack to work around poor bigdecimal support
val mc = new java.math.MathContext(300,java.math.RoundingMode.HALF_UP)
def divide(a:BigDecimal, b:BigDecimal) = {
new BigDecimal(a.bigDecimal.divide(b.bigDecimal, mc))
}

def piSummands(n: BigDecimal): Stream[BigDecimal] = {
divide(M(1), n) :: piSummands(n + M(2)).map(_ * -1)
}

def scaleStream(stream:Stream[BigDecimal], factor:Int) = {
stream map {_ * factor}
}

def addStreams(s1:Stream[BigDecimal], s2:Stream[BigDecimal]) = {
s1 zip(s2) map {z => z._1 + z._2}
}

def partialSums(s:Stream[BigDecimal]):Stream[BigDecimal] = {
s.head :: addStreams(s.tail, partialSums(s))
}

def piStream = scaleStream(partialSums(piSummands(1)), 4)

def square(s:BigDecimal) = s * s

def eulerTransform(s:Stream[BigDecimal]):Stream[BigDecimal] = {
val s0 = s(0)
val s1 = s(1)
val s2 = s(2)
val head = s2 - divide((square(s2 - s1)), ((s0 + (M(-2) * s1) + s2)))
head :: eulerTransform(s.tail)
}

type Transform = (Stream[BigDecimal]) => Stream[BigDecimal]

def makeTableau(transform: Transform, s:Stream[BigDecimal]):Stream[Stream[BigDecimal]] = {
s :: makeTableau(transform, transform(s))
}

def acceleratedSequence(transform:Transform, s:Stream[BigDecimal]) = {
makeTableau(transform, s) map {_.head}
}

val set = acceleratedSequence(eulerTransform, piStream)
println(set.take(150).last)

Monday, August 3, 2009

Rails threadsafe! and Engines

If you're planning to use the combination of engines and threadsafe! you'll need to do some extra work. Due to a bug in Rails the engine load paths are not added to the eager_load_paths at startup time. This makes engines unavailable in threadsafe! mode.

Fortunately, working around this problem is simple. Just subclass the plugin loader like this:



1
2
3
4
5
6
7
8
class EagerLoader < Rails::Plugin::Loader
def add_plugin_load_paths
super
engines.each do |engine|
configuration.eager_load_paths += engine.load_paths
end
end
end

And wire up your new loader in your environment.rb:



1
2
3
4
5
6
7
8
9
#require the plugin loader
require File.join(File.dirname(__FILE__), '..', 'lib', 'eager_loader')

Rails::Initializer.run do |config|
#override the default loader
config.plugin_loader = EagerLoader

# ...
end

Tuesday, May 5, 2009

Mathematicians should never code alone.

Case in point:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (xGrad * yGrad <= (float) 0 /*(1)*/
? Math.abs(xGrad) >= Math.abs(yGrad) /*(2)*/
? (tmp = Math.abs(xGrad * gradMag)) >= Math.abs(yGrad * neMag - (xGrad + yGrad) * eMag) /*(3)*/
&& tmp > Math.abs(yGrad * swMag - (xGrad + yGrad) * wMag) /*(4)*/
: (tmp = Math.abs(yGrad * gradMag)) >= Math.abs(xGrad * neMag - (yGrad + xGrad) * nMag) /*(3)*/
&& tmp > Math.abs(xGrad * swMag - (yGrad + xGrad) * sMag) /*(4)*/
: Math.abs(xGrad) >= Math.abs(yGrad) /*(2)*/
? (tmp = Math.abs(xGrad * gradMag)) >= Math.abs(yGrad * seMag + (xGrad - yGrad) * eMag) /*(3)*/
&& tmp > Math.abs(yGrad * nwMag + (xGrad - yGrad) * wMag) /*(4)*/
: (tmp = Math.abs(yGrad * gradMag)) >= Math.abs(xGrad * seMag + (yGrad - xGrad) * sMag) /*(3)*/
&& tmp > Math.abs(xGrad * nwMag + (yGrad - xGrad) * nMag) /*(4)*/
) {
magnitude[index] = gradMag >= MAGNITUDE_LIMIT ? MAGNITUDE_MAX : (int) (MAGNITUDE_SCALE * gradMag);
//NOTE: The orientation of the edge is not employed by this
//implementation. It is a simple matter to compute it at
//this point as: Math.atan2(yGrad, xGrad);
} else {
magnitude[index] = 0;
}

Thursday, April 30, 2009

Ruby's object.methods reflection in Scala

Update: Just found out the Scala REPL in trunk has tab completion, which does pretty much exactly this with a lot less fuss. Cool.

If you're coming from the Ruby world to learn Scala you'll learn quickly how much you miss Ruby's elegant reflection. On day 5 of my Scala adventure I took a crack at using Java's reflection APIs to do the job. I think it worked out pretty well. I'd love to hear what you think about it.

get the code here



1
2
3
4
5
//names of all methods, including super classes
namesOf(methods("Foo"))

//signatures of all methods, excluding super classes
signaturesOf(declaredMethods("Foo"))

Wednesday, April 29, 2009

Tuesday, April 21, 2009

.:format feature breaks URL compliance (rfc1738) in Rails

I put in a lighthouse ticket in Rails which you can read here. +1 me if you agree.

Tuesday, March 31, 2009

Software Craftsmanship > Certification

There's been a lot of discussion about Agile Certification, a Rails Maturity Model, and some such. Are we always doomed to repeat the mistakes of the past? Remember the MS/Java/Sun/Linux certification craze of a few years ago? Yeah, same thing.

I did some hiring back then. I'd float right over any resume that prominently listed a big set of certifications because I thought people that misguided couldn't have a clue as to how real software systems are built. Mostly I think I was right. The first resume I see sporting a Rails or Agile certification is going to get framed and put on The Wall of Shame. I don't have a Wall of Shame, but I'll create one just for it. I promise.

For those of you coming into this industry, coming into Agile or Rails. Or for those somewhat experienced fellows looking for what to do next. Certification is not the answer. Let me now bestow upon you the few truths I know about this:

First.
I can tell you with much certainty that there is only one way to learn how to build great software: from a master. I read this Chinese proverb once and it stuck with me:

A single conversation with a wise man is better than ten years of study.

A bit of an exaggeration, I know, but truth none the less. Nothing compares to working day in and day out with a master of the craft. Literature, conferences, training. They're all good places to start, but they are not enough. You need a mentor, the best one you can find.

Second. It will take you years of hard work to become generally competent. I've been at it for about seven years now and I feel as if I've climbed to stand upon one great peak of knowledge only to look down on the vastness of my ignorance. Yes that was as bit dramatic. I apologize.

Third. The only way to gauge the true skill of a craftsman (that's you) or organization is by their work and the opinions of their colleges. Not where you got your graduate degree, especially not the number of certifications pinned to your wall.

Now that I have you convinced. What can you do? How can you apprentice yourself to a master?

Most are not terribly hard to find. They write lots of books, they have blogs, they speak at conferences. Find one who's style resonates with you, then figure out how to get your foot in the door wherever their opinions hold sway. If it does not work out, find someone else and try again. And again.

If you wanted to get your foot in the door with us, for instance, you can undertake an apprenticeship. Otherwise, There are a lot of great places to learn this craft beyond our doors. I know this list is short, but here's the few companies I can recommend (mostly in the Chicago area): Obtiva, 8th light, Atomic Object, Object Mentor, ThoughtWorks.

Friday, March 6, 2009

Join the Software Craftsmanship community!

Interested in Software Craftsmanship but haven't found a good peer group to share your thoughts with? Then this google group is for you.

http://groups.google.com/group/software_craftsmanship/

Thursday, February 26, 2009

Watch Paul Graham write an essay

Neat technology. It is fascinating to see how often phrases are re-worded and paragraphs completely restructured. The essay isn't half bad either.

http://etherpad.com/ep/pad/slider/13sentences

Wednesday, January 28, 2009

Expand your mind into the Tenth Dimension!

http://www.break.com/index/how-to-imagine-the-tenth-dimension.html