Articles Feed

Authors

Categories

Limelight Tutorial: Tic Tac Toe Example

by: paul | September 29th, 2008 | 0 comments »

Welcome to a Limelight production. I am going to go through a step by step introduction to limelight development using a tic tac toe game as an example. So, lets get started. I am going to create the directory structure and open it up in a text editor.

  1. $ mkdir tictactoe
  2. $ cd tictactoe
  3. $ mate .

Now I need to set up Limelight. You can just download the gem.

  1. $ jruby -S gem install limelight

We can start by creating the props.rb file in the tictactoe directory. The props.rb file defines the structure of your application. A prop is named after the theater metaphor. We are going to use them to define what our scene's physical structure look like. We can start with a simple screen with an empty board with the nine cells we need for a tic tac toe game. Lets create a spec directory to write a test for the props we are going to create.

  1. $ mkdir spec
  2. $ mkdir spec/props

Now for the spec. In the spec directory, we can name our spec props_spec.rb. We want to check that there is a cell on the scene. Here is the first test. NOTE: To be able to run the test, you will need the spec_helper.rb in your spec directory (not the props directory). You can copy it from the sample application.

  1. require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
  2. describe "Props" do
  3. include PropSpecHelper
  4. before(:each) do
  5. setup_prop_test
  6. end
  7. it "should have cell_0_0" do
  8. @scene.find("cell_0_0").should_not be(nil)
  9. end
  10. end

and when we run it (You can copy the Rakefile from the sample application as well, if you want to have a specs task),

  1. $ jruby -S rake spec

we get the failure

1) Errno::ENOENT in 'Props should have cell_0_0' No such file or directory - File not found - /Users/paulwpagel/Desktop/tictactoe/props.rb /Users/paulwpagel/Desktop/tictactoe/spec/spec_helper.rb:18:in `initialize' /Users/paulwpagel/Desktop/tictactoe/spec/spec_helper.rb:18:in `setup_prop_test' /Users/paulwpagel/Desktop/tictactoe/./spec/props/props_spec.rb:6: Finished in 0.063 seconds 1 example, 1 failure

So, lets create the props.rb file in the project root. Now we should get the error.

1) 'Props should have cell_0_0' FAILED expected not nil, got nil /Users/paulwpagel/Projects/tictac/./spec/props/prop_spec.rb:12: Finished in 0.089 seconds 1 example, 1 failure

Each of the props accepts a block of code your can give options/structure to. We can open the props.rb file and add a cell with the id of "cell_0_0" to make this test pass.

  1. main do
  2. board do
  3. cell :id => "cell_0_0"
  4. end
  5. end

And the test passes. Lets make sure we have the rest of the id's while we are at it. Here is a more exhaustive spec.

  1. it "should have cells" do
  2. @scene.find("cell_0_0").should_not be(nil)
  3. @scene.find("cell_0_1").should_not be(nil)
  4. @scene.find("cell_0_2").should_not be(nil)
  5. @scene.find("cell_1_0").should_not be(nil)
  6. @scene.find("cell_1_1").should_not be(nil)
  7. @scene.find("cell_1_2").should_not be(nil)
  8. @scene.find("cell_2_0").should_not be(nil)
  9. @scene.find("cell_2_1").should_not be(nil)
  10. @scene.find("cell_2_2").should_not be(nil)
  11. end

And it fails in a similar manner. Lets expand our props.rb file to make the test pass.

  1. main do
  2. board do
  3. cell :id => "cell_0_0"
  4. cell :id => "cell_0_1"
  5. cell :id => "cell_0_2"
  6. cell :id => "cell_1_0"
  7. cell :id => "cell_1_1"
  8. cell :id => "cell_1_2"
  9. cell :id => "cell_2_0"
  10. cell :id => "cell_2_1"
  11. cell :id => "cell_2_2"
  12. end
  13. end

And it passes. However, it is all ruby code, so I can leverage ruby functions to help me out. Lets remove the duplication.

  1. main do
  2. board do
  3. 3.times do |row|
  4. 3.times do |col|
  5. cell :id => "cell_#{row}_#{col}"
  6. end
  7. end
  8. end
  9. end

Much better. Lets now move on to the styles. Nothing will show up without a few styles. I create a styles.rb file in the project root and filled it with some simple content. In Limelight, styles refer to how a prop is aesthetically displayed on the screen. Here is an example which defines the size and gives a border to the board and the cells.

  1. board {
  2. width 152
  3. height 152
  4. border_width 1
  5. border_color "black"
  6. }
  7. cell {
  8. width 50
  9. height 50
  10. border_width 1
  11. border_color "black"
  12. }

We should be able to start up Limelight and see the board. We start Limelight like: (From the tictacctoe directory)

  1. $ jruby -S limelight open .

and there is your first Limelight screen. Pretty easy, and all ruby code. Lets make it more interesting. Let us make it such that if you click on one of the squares, the square shows the 'X' mark denoting the first move.

First we create a directory called players. Inside go the players, which contain the actions and behavior of the props for a Limelight scene (the controllers).

  1. $ mkdir players

We want to now make a player for the cell prop. We create a file inside of the players directory called cell.rb. The file will start with a definition by looking like:

  1. module Cell
  2. end

We define all players in modules of the same name as the file and prop, by convention. This allows Limelight to include this behavior when it needs it. You can specify specific mappings between the props and its players, but we don't need to do that here. So, let's make the cell more interesting. When we click on the cell, we want it to make a large 'X' mark. Lets start by creating a spec for the behavior.

I created a new directory for the players spec

  1. $ mkdir spec/players

We have to add the players directory to the ruby search path, so I added the following line to the spec_helper.

  1. $: << File.expand_path(File.dirname(__FILE__) + "/../players")

My spec is going to find the prop that was clicked on and make that prop display an 'X', denoting the first move. Here is what my first spec looks like (I call it cell_spec.rb):

  1. require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
  2. require 'cell'
  3. describe Cell do
  4. include Cell
  5. attr_accessor :id
  6. it "should make first move an X" do
  7. @id = "cell_0_0"
  8. @cell_one = Limelight::Prop.new
  9. @scene = MockScene.new
  10. @scene.register("cell_0_0", @cell_one)
  11. self.stub!(:scene).and_return(@scene)
  12. mouse_clicked(nil)
  13. @cell_one.text.should == "X"
  14. end
  15. end

Which provides the feedback when run:

F 1) NoMethodError in 'Cell should make first move an X' undefined method `mouse_clicked' for # /Users/paulwpagel/Projects/tictac/spec/players/cell_spec.rb:14: Finished in 0.007423 seconds 1 example, 1 failure

If you have seen a rSpec specification before, this should look syntactically familiar. Before we move on to making the test pass, let us take closer look at a few aspects.

@id = "cell_1_1" - This line is setting the id of the imaginary prop that the players behavior will be executed against. @scene = MockScene.new - This creates a scene to mock out. The scene will be explained later, but for this test we are going to use the find method on scene to find our props. @cell_one = MockProp.new - Create a mock prop that will turn to 'X' when clicked @scene.register("cell_1_1", @cell_one) - We are giving the scene the mock prop, so the find method will find it by its id. mouse_clicked(nil) - Simulates a mouse_click on the cell. It takes an event, but we don't care about that yet, so lets just pass in nil.

All right, time to make this test pass. Lets open up the cell.rb player and see what we need done to make the test pass.

  1. module Cell
  2. def mouse_clicked(event)
  3. cell_prop = scene.find(id)
  4. cell_prop.text = "X"
  5. end
  6. end

Run the test again, no failures. We needed to find the prop on the screen which we are concerned about. We do this by calling find on a method scene, which will give us any prop by its unique identifier. We are looking for the id of the element we clicked, and then we set the text of that element to 'X', which makes the test satisfied.

Now, we can run the application from the root directory.

  1. $ jruby -S limelight open .

If we click on the box that is displayed, a small 'X' should appear in the upper right corner.

Congratulations, that is your first piece of Limelight behavior. However, this is not very interesting yet. Lets take it the next step and make the tic tac toe game work. I am going to create a lib directory to hold the game model.

  1. $ mkdir lib
  2. $ mkdir spec/lib

And before I write my first spec, I am going to add the new lib directory to the ruby search path by adding the following line to the spec_helper (It is already in the example spec_helper.rb, you don't need to add it).

  1. $: << File.expand_path(File.dirname(__FILE__) + "/../lib")

So, here is what my first spec looks like:

  1. require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
  2. require 'game'
  3. describe Game do
  4. it "make a move in the middle square" do
  5. game = Game.new
  6. game.move(1, 1)
  7. game.mark_at(1, 1).should == "X"
  8. end
  9. end

To make it pass, we need to create a game class in the lib directory and give it this code.

  1. class Game
  2. def move(row, column)
  3. end
  4. def mark_at(row, column)
  5. return "X"
  6. end
  7. end

And we can follow the test driving of the model to make the game class. I have already done this, and you can download the models in the sample application. Lets move past that back to the players and hook up the game.

I am going to create a file init.rb in the root directory. The init.rb class gets loaded up by Limelight when you start the application. We want to create a new game and have a way to keep it in memory for the other classes to use. Here is what the spec looks like in a init_spec.rb in the spec directory:

  1. require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
  2. require "game"
  3. describe "init" do
  4. it "should create new game on initialization" do
  5. game = mock('game')
  6. Game.should_receive(:new).and_return(game)
  7. Game.should_receive(:current=).with(game)
  8. require File.expand_path(File.dirname(__FILE__) + "/../init")
  9. end
  10. end

The simplest way to start that is to have a current_game class variable. Here is the code for the init.rb.

  1. $: << File.expand_path(File.dirname(__FILE__) + "/lib")
  2. require "game"
  3. Game.current = Game.new

I add the lib directory to the ruby search path so the Limelight application would know what a game is when I require it.

Now we need to plug the game model into the cell player. Lets change the spec we made earlier to make the first move depending on the game model. Here is the new version.

  1. it "should make first move in a game" do
  2. @id = "cell_0_0"
  3. @cell_one = Limelight::Prop.new
  4. @scene = MockScene.new
  5. @scene.register("cell_0_0", @cell_one)
  6. self.stub!(:scene).and_return(@scene)
  7. game = mock('game')
  8. Game.should_receive(:current).and_return(game)
  9. game.should_receive(:move).with(0, 0)
  10. game.should_receive(:mark).and_return("X")
  11. mouse_clicked(nil)
  12. @cell_one.text.should == "X"
  13. end

I am mocking out the game model and passing the values from the id into the game's move method. Here is the code that makes this pass.

  1. module Cell
  2. def mouse_clicked( event)
  3. game = Game.current
  4. x, y = get_coordinates
  5. game.move(x, y)
  6. cell_prop = scene.find(id)
  7. cell_prop.text = game.mark
  8. end
  9. private ################################
  10. def get_coordinates()
  11. x = id[(id.length - 1)..(id.length - 1)].to_i
  12. y = id[(id.length - 3)..(id.length - 3)].to_i
  13. return x, y
  14. end
  15. end

Minus the ugly string manipulation, it is a pretty straight forward approach. Now we should be able to start up the application and click on any of the squares and make some moves. There are 2 things left to do for this demo. We need to make sure that a player can not move on a square that is already occupied, and we need to display a winner. So for the first task, we need to write a spec to have some kind of feedback to the players that the move is invalid. Let's add this spec to the props_spec file.

  1. it "should have message center for feedback to the user" do
  2. @scene.find("message_center").should_not be(nil)
  3. end

Nice and simple. Here is the new props.rb file.

  1. main do
  2. board do
  3. 3.times do |row|
  4. 3.times do |col|
  5. cell :id => "cell_#{row}_#{col}"
  6. end
  7. end
  8. end
  9. end
  10. message_center :id => "message_center"

Now lets write a spec for the cell_spec to make sure that the move is valid, else we display a message in the message center to the user they must move somewhere else. Here is the spec.

  1. it "should display in the message center if the space is occupied." do
  2. @id = "cell_0_0"
  3. @cell_one = Limelight::Prop.new
  4. @message_center = Limelight::Prop.new
  5. @scene = MockScene.new
  6. @scene.register("cell_0_0", @cell_one)
  7. @scene.register("message_center", @message_center)
  8. self.stub!(:scene).and_return(@scene)
  9. game = mock('game')
  10. Game.should_receive(:occupied?).with(0, 0).and_return(true)
  11. mouse_clicked(nil)
  12. @message_center.text.should == "This space is occupied, please move in an unoccupied square"
  13. end

Same as before, with a new prop added. Here is the new cell.rb file.

  1. module Cell
  2. def mouse_clicked( event)
  3. game = Game.current
  4. x, y = get_coordinates
  5. if game.occupied?(x, y)
  6. message_center = scene.find("message_center")
  7. message_center.text = "This space is occupied, please move in an unoccupied square"
  8. else
  9. game.move(x, y)
  10. cell_prop = scene.find(id)
  11. cell_prop.text = game.mark
  12. end
  13. end
  14. private ################################
  15. def get_coordinates()
  16. x = id[(id.length - 1)..(id.length - 1)].to_i
  17. y = id[(id.length - 3)..(id.length - 3)].to_i
  18. return x, y
  19. end
  20. end

Simple if, makes it all work. Lets remove the duplication in the specs. Here is the new spec file.

  1. require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
  2. require 'cell'
  3. describe Cell do
  4. include Cell
  5. attr_accessor :id
  6. before(:each) do
  7. @id = "cell_0_0"
  8. @cell_one = Limelight::Prop.new
  9. @scene = MockScene.new
  10. @message_center = Limelight::Prop.new
  11. @scene.register("message_center", @message_center)
  12. @scene.register("cell_0_0", @cell_one)
  13. self.stub!(:scene).and_return(@scene)
  14. @game = mock('game', :occupied? => false)
  15. Game.should_receive(:current).and_return(@game)
  16. end
  17. it "should make first move in a game" do
  18. @game.should_receive(:move).with(0, 0)
  19. @game.should_receive(:mark).and_return("X")
  20. mouse_clicked(nil)
  21. @cell_one.text.should == "X"
  22. end
  23. it "should display in the message center if the space is occupied." do
  24. @game.should_receive(:occupied?).with(0, 0).and_return(true)
  25. mouse_clicked(nil)
  26. @message_center.text.should == "This space is occupied, please move in an unoccupied square"
  27. end
  28. end

Much better. Now lets do the case of a winner. Here is the spec for the cell.

  1. it "should display there was a winner in the message center" do
  2. @game.should_receive(:move).with(0, 0)
  3. @game.should_receive(:is_winner?).and_return(true)
  4. mouse_clicked(nil)
  5. @message_center.text.should == "Player X has won the game, congratulations"
  6. end

And here is the new cell.rb

  1. module Cell
  2. def mouse_clicked( event)
  3. game = Game.current
  4. x, y = get_coordinates
  5. if game.occupied?(x, y)
  6. message_center.text = "This space is occupied, please move in an unoccupied square"
  7. else
  8. game.move(x, y)
  9. cell_prop = scene.find(id)
  10. cell_prop.text = game.mark
  11. message_center.text = "Player #{game.mark} has won the game, congratulations" if game.is_winner?
  12. end
  13. end
  14. private ################################
  15. def get_coordinates()
  16. x = id[(id.length - 1)..(id.length - 1)].to_i
  17. y = id[(id.length - 3)..(id.length - 3)].to_i
  18. return x, y
  19. end
  20. def message_center
  21. return scene.find("message_center")
  22. end
  23. end

Now we can finish off the application by adding new game functionality, or even a computer player that can not be beaten! However, before I let you go, we have to add some styles to the message center and pretty up the board to make it look better. To find a comprehensive list of the styles supported in Limelight, go here(http://limelightwiki.8thlight.com/index.php/Style_Attributes). Here is a new version of the styles.rb.

  1. main {
  2. width "100%"
  3. horizontal_alignment "center"
  4. }
  5. board {
  6. width 152
  7. height 152
  8. border_width 1
  9. border_color "black"
  10. }
  11. cell {
  12. width 50
  13. height 50
  14. border_width 1
  15. border_color "black"
  16. }
  17. message_center_container{
  18. top_margin 100
  19. width "100%"
  20. horizontal_alignment "center"
  21. }
  22. message_center {
  23. width 300
  24. height 100
  25. rounded_corner_radius "10"
  26. border_color "black"
  27. border_width 2
  28. padding 5
  29. }

There was one change to the props.rb file, to wrap the message_center in a prop called message_center_container. Also, notice the pretty rounded corners. Easy to do.

Here is the props.rb

  1. main do
  2. board do
  3. 3.times do |row|
  4. 3.times do |col|
  5. cell :id => "cell_#{row}_#{col}"
  6. end
  7. end
  8. end
  9. end
  10. message_center_container do
  11. message_center :id => "message_center"
  12. end

Happy Limelight coding!

Customer Interaction

by: | September 23rd, 2008 | 0 comments »

At 8th Light, they follow a practice of training people through an apprenticeship period. As it is nearing the end of my apprenticeship, my mentor has asked me to write a blog talking about one thing I have learned during the course of my apprenticeship. For me, the one skill that I learned the most about was the one skill that needed the most work in the first place. The main thing I learned was that I was writing software for others. Similar to that idea came the need for increased customer interaction and learning how to interact with people in general.

Prior to working at 8th Light, almost all the programming I had ever done was either for myself, or for school projects. In the first case, I was my own user, so there was never any issue in communication there. And for the second case, the requirements were almost always very clearly drawn out(and not likely to change). Again, not much room for error. Once I came to 8th Light, I was forced to write code both specified and to be used by someone that was not myself. This was something new to deal with. For at least the first month or so, my thought process was for myself. It wasn't until I came to that realization that I was able to change my thought process. There was not one single event that made me learn this, but it was something learned over time. When starting a story, once the requirements have been approved, I don't try to change the story. Even if I don't agree with the desired functionality, it is what the customer wanted, and that's all that matters. And now, when I finish a story, I take the time review the requirements and make sure it completes all the requirements that the customer specified.

In order to provide a high quality product for another person, you must maintain a high level of collaboration with them. Without talking to them, you will never be able to guess what they want. So, in going along with the first lesson, I also learned that a lot of customer interaction is important and any issues that arise should be addressed quickly. One event in particular occurred that helped me realize this was the week leading up to one of our releases. It was around this point that the customer decided to begin some testing of the system and uncovered a number of bugs. There was some concern as to whether or not all the bugs were being kept track of in some way, and this added stress to both the development team and the customer. It wasn't the bugs that taught me anything, but the manner in which Micah handled them. After our iteration meeting, he called attention to the problem. He didn't blame anybody, but just said that something needed to be done to fix the problem and even proposed some possible solutions. It was the high level of communication between the development team and the customers that was valuable to me. This made me realize that you have to address problems quickly, and it helps to offer multiple solutions to a problem. The customer also seemed to appreciate that we brought the issue up, and partly because of this week of bugs, we introduced a new bug tracking tool to our process.

I also began to learn that if you have a question about how something is supposed to work, sometimes the best solution is to just ask the customer. At one of our recent iteration meetings, the customer mentioned functionality that they expected to be in a completed story that had never been discussed. We realized that there was not enough feedback gained from the requirements, so we altered our process to account for that. Now, after determining the requirements, we bring them to the customer and discuss them in person. This is something we just began, but it already has shown signs of success. The customer has actually come to us to discuss the requirements for some of the stories. It is because our team values communication that we were able to improve our process.

Learning these things has already helped me work more productively. If I have a question, instead of trying to spend too much time reasoning it out my own, it can be much faster to either go to the requirements or directly to the customer. During the course of my apprenticeship, I also learned a lot about coding but nothing was as important as what I learned about human interaction. This is a lesson that has definitely changed my mentality towards coding and will hopefully improve my skills as a professional software craftsman.

Definition of Software Craftsman

by: micah | September 21st, 2008 | 5 comments »

Craftsman Clarification

There has been some discrepancy in the use of the term “software craftsman”. Rather than going into details about various uses of the term, I’ll just clarify what I believe it means.

software craftsman n. A man who practices the software craft.

There are a few points to make about this definition.

1. “software craft”

A craftsman believes that software is a craft. This is important because not everyone believes this. A craftsman takes pride in his work an strives to do the best job he can. He believes that writing good software requires skill and careful attention. That software is not something that can be manufactured nor can it be delivered faster by merely adding more bodies.

2. “practices”

A craftsman practices his craft, always striving to become more skillful, to produce better software.

There are traditionally 3 stages of craftsmanship:

  1. Apprentice
  2. Journeyman
  3. Master

No matter which stage one may be in, as long as he practices software as a craft, he is a craftsman.

3. “A man”

Technically the term “craftsman” is gender specific. Women are just as capable of software craftsmanship. Indeed, I’d like to see more software craftswomen out there. In an effort not to alienate anyone we should use the term “software craftsperson” more liberally.

Update: There’s a movement afoot to make the term “software craftsman” gender neutral. Feel free to comment below.

I’ve checked with the book “Software Craftsmanship” by Pete McBreen to see if it conflicts with my definition. Although, he uses the term “software craftsman” ambiguously at times, he is careful to use the term “master craftsman” when referring to craftsmen at the height of his craft. This is in line with my definition.

I hope this serves as a reference for my use of the term. People should not think me presumptuous when I call myself or my colleagues craftsmen. I mean only what I describe above.

Selenium on Rails example

by: eric | September 20th, 2008 | 0 comments »

The files for the WindyCityRails talk here.

Own Your Tools

by: doug | September 15th, 2008 | 2 comments »

I spent a lot of time with my grandpa growing up. He has spent much of his retirement ‘fooling around’ in a workshop he built on his slice of Pike County, Illinois farmland. From a very early age I spent a lot of weekends and entire weeks of the summer as Grandpa’s little helper. I’d work on the things he was working on while helping and learning. When I was about 7 years old, I was helping Grandpa refinish a dresser. I had a flat paint scraper in my little hands and was trying to dig into the layers of flaking paint that gripped the dresser. I really didn’t have the strength to get under the paint with that big blade, so I turned the tool on edge and started scratching deeply into the wood. I was making progress now, but tearing the heck out of the surface. Now in Grandpa’s version of this story, he says that when he saw what I was doing he corrected me too harshly and sharply and later found me teared up in a corner of the workshop sorry for what I’d done. I honestly don’t remember being that hurt by his rebuke, but I do know how to use a paint scraper and dozens of other tools properly because of that time I spent ‘helping’ grandpa in his shop.

As software crafts-people our tools are other bits of software. We spend every minute of every day using software. Our editors, compilers, interpreters, source code repositories, IDEs, debuggers, and test frameworks enable every stage of our work. Everything we do to create and add value to a software project requires some other software tool to make it happen.

At many of the places I’ve worked, the developers have a laundry list of complaints about their software tools. Often, teams develop elaborate rituals and incantations to dance around the shortcomings of their tools. “Before you check in, copy this file over there then add a note to that file then spin around in your chair three times and your check-in should succeed.” Now I agree that there are lots of poorly designed tools out there. Some tools do things that just don’t make sense and others crash all the time, but I have a challenge for us as software crafts-people: Own your tools. Tools are software, you are a software crafts-person. You can find or make a tool to do anything you need.

Groups decide to invest in a tool, often at great cost, but very few people end up understanding or even using the tool. The tools usually gets blamed for its shortcomings before anyone has really made the effort to get them to work. They give up on it far to early. Own your tools. Understand everything it can do, and if it isn’t enough, find a way to make it do what you want.

One of the cool things that happens is that investing time in your tools may give you the chance to explore a new language. One of our goals as craftsmen at 8th Light is to learn a new language every year, but often its hard to justify the time spent in a new language when you are heads-down in a project. Using a new language to create a tool for yourself may give you just the excuse you need to spread out a bit and learn something new.

Is it really worth it? Can the time spent writing “non-production” code really be justified? Let’s take a task, say the creation of a new class in C++. Most people when creating a new class, will open up the last class they worked on, copy the entire thing, paste it into a new file, delete all the methods and data, then do a find/replace on the class name. There are probably a few comments that need editing, the revision history needs to be cleaned out and maybe there is a comment header or footer that needs to be updated. This entire process could take maybe 2 minutes. It hardly seems like it’s worth automating. Except … Wait! It won’t compile, because you mistakenly didn’t remove the parameters from the constructor and the compiler can’t resolve the dependencies. This is a manual process, so it’s error prone. So you spend maybe another 3 minutes getting the thing to compile. Then we need a test harness so you do the same copy, paste, delete, find / replace process. 8 minutes later we have a new class and test harness and we are ready to start working. The hour or so investment to build a tool for yourself will surely be worth it now. If you share your work with the rest of your team and suddenly everyone is saving 8 minutes every time a new class is generated, you might have your hour back by the end of the week.

Some IDEs and editors have some class generation utilities for you. This may get you part of the way there, but it probably won’t put your company’s header on the top or put in your source control keywords. It may not make your destructor virtual or create a private copy constructor to start like I like to do. The tool doesn’t do exactly what you want. Own your tool. You are a software craftsmen. You can create software that generates a class for yourself. If you are in an editor that supports macros or bundles, you may be able to implement the tool in the editor’s macro language.

In this spirit, the craftsmen at 8th light have contributed to open source software tool projects for several years. Micah is the author of the widely used Fitnesse acceptance test framework and Eric has taken over the management of Selenium on Rails. Micah’s also been working on a whole new development framework called LimeLight. We love open source tools because they let anyone own the tool.

Grandpa used to make things out of sliced walnuts. He’d glue together little baskets or crosses and fill the walnut cavities with colorful beads. As you can imagine, a walnut is a pretty difficult thing to slice. At the very least it’s awfully dangerous to get your fingers that close to a band saw. Grandpa showed me a while ago the tool that he made to save his lower digits. It’s a pretty simple block of wood with a half-walnut sized divot carved out of it. It was a woodworker using his woodworking skills to create (and own) a tool for woodworking.

If you’d like my class generator, you can have it here. Chances are, it won’t do exactly what you need it to do, so … own your tool.

Selenium on Rails released

by: eric | September 14th, 2008 | 1 comments »

Remember when I used to post here? Not that I’ve ever been prolific but there is a reason I haven’t posted in 53 days, well at least the last 30, as I’ve been hard at work preparing for my Selenium on Rails talk at Windy City Rails. What work? Primarily releasing a new version, one that works on Rails 2.1. These are the newest features:

During the last month I’ve taken the official repository at OpenQA and cloned it at git. While I’ll still be maintaining the OpenQA version because I like the association with the group, I find it’s hard to get help with contributions on the site through their subversion repository. Therefore I’ve created a github for it that will be periodically synchronized with the subversion repo. If you want to help by contribution features or patches, or you want the latest and greatest repo, head here: http://github.com/paytonrules/selenium-on-rails/tree/master. Otherwise it’s still hosted at its normal location here: http://selenium-on-rails.openqa.org/. There’s still an old version available for Rails users that haven’t upgraded, and the instructions for installation are available as well.

Please let me know if you got feature requests for Selenium On Rails, or patches ready to go. I’m looking forward to revitalizing that project.

######