Mon 06 August, 2007

add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
What’s now commonly called “punk rock” arguably started in New York City around 1974, when bands such as The Ramones and Television took up residence at CBGB’s. However, it was still pretty far underground, and t wasn’t until a few years later that the NYC music scene hit its stride (sparking similar eruptions of audio angst across the States and in the U.K.).
VH1 (oddly enough) is scheduled to show a documentary of those times, NY77:The Coolest Year in Hell
I’m stoked to see this, partly because I was there at the time (so I’m waxing nostalgic), but mostly because my good friend Patty has some film footage included.
This two-part, two-hour documentary tells the story of one of the most astonishing pop culture years in American history. New York City had fallen in decay and chaos. There were not enough jobs, not enough money, not enough police, not enough schools, and not enough social services. There was a city wide black out with major looting, there was a serial killer on the loose, and the Bronx was burning.
Yet out of the chaos, emerged one of the most creative times any city has ever encountered. Hip Hop was emerging from the South Bronx, punk music was emerging from the Lower Eastside, and disco was emerging from Queens and midtown Manhattan. Elaborate, finely crafted graffiti art decorated the subway cars. Break-dancers danced in the streets. There was a huge sexual liberation with sex clubs and a burgeoning porn industry. In the beginning of the year, the world was not paying attention, and most of this activity existed in its own underground bubble. Yet by the end of 1977 all of this artistic expression was about to become part of mainstream America and would remain popular for generations to come. Maybe it would never again be this independent expression, not invented for money or fame, but the need to rebel against the mayhem around them. Maybe it would go on to be commercialized and sterilized for massive consumption. Maybe it would never again be this unique.
I’m trying to be optimistic that it won’t suck too much, and hopefully it will capture the spirit of the day and get the story straight.
add to del.icio.us. look up in del.icio.us.
add to furl
Grad school & Caprice
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furlSun 05 August, 2007

add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
I want to thank Derek Neighbors and Integrum for yesterday’s Phoenix Super Happy Dev House . It was a blast.
I also want to thank those who attended; I’m know it was a bit of a drive for many (40 miles for me. Each away. :) ) but supporting these kinds of gatherings is important in bolstering the local developer community.
The demos were outstanding, as were the ad hoc discussions and hacking sessions. I especially want to thank Brian Shaler (yes, THE Brian Shaler) for showing off his Google Gears work and helping me sort out a Gears + Greasemonkey issue I had.
add to del.icio.us. look up in del.icio.us.
add to furl
“ That was the heaviest slam we’ve ever seen. ”
Commentator’s response to Jake Brown’s fall
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
2. Zoology To pass the summer in a dormant or torpid state.
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
“ Experience of object oriented programming languages is not considered a merit. ”
From the qualifications section of an Erlang job listing
add to del.icio.us. look up in del.icio.us.
add to furlSat 04 August, 2007

In der Stufe haben wir ja einige recht aktive Blogger, und man kommt kaum nach mit lesen. Statt alles in mein NetNewsWire zu importieren suchte ich nach einem öffentlichen Feedaggregator und bin dann bei SuprGlu gelandet.
Sämtliche Blogs der Stufe kann man jetzt also gesammelt auf
lesen.
Wer noch andere stufenrelevante Blogs hat, bitte bei mir melden.
NP: Bob Dylan—Let Me Die in My Footsteps
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
“ I just gave myself commit rights to the English language. ”
Patrick Ewing
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
Ray Charles — Ring of Fire on the Johnny Cash Show
add to del.icio.us. look up in del.icio.us.
add to furl
Tip Wild card matching Google queries
An asterisk can be used in a Google query as a wild card placeholder in a search phrase.
For example, if you forgot how to spell “Dwemthy”, a search for:
”*’s array” why the lucky stiff
finds results for “Dwemthy’s Array”.
Or, for example, you could try:
“Yukihiro Matsumoto was born in *“
(Though “Yukihiro Matsumoto was born in” might actually be better depending on what you are looking for…)
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
“ Good feeling of oneness with cup rubber. ”
HHKB Pro 2 box
add to del.icio.us. look up in del.icio.us.
add to furl
This is a fun example of using FlexMock
Andrew Sweeney Asks:
Andrew Sweeney emailed me with the following question:
I am currently working on a ruby project in which I think flexmock would be a good fit for unit testing. I have read the documentation and gone over the examples however fail to wrap my head around how to apply flexmock to my own app. I was hoping that you could give me some guidence and get me started or point me in the right direction.
You can find his original source code here.
I thought his problem was interesting enough to write it up as an example of using FlexMock. Andrew and his mentor, Bil Kleb gave permission for me to reproduce the code in my blog. The F3DQueue class is part of a Computational Fluid Dynamics project (http://fun3d.larc.nasa.gov) at NASA.
Quick Code Review
The F3DQueue class is small, so there’s not a lot of code we need to wade through. We see it uses a second class named AutoF3D, but the only clues we have to what AutoF3D might do are the four method calls on the “job” object in the run method.
It looks like the main interface to the queue object is the add_to_queue method. There is a thread started that pulls jobs (i.e. AutoF3D objects) from the queue and processes them in turn. There is some server delays built into the system. I presume that Computational Fluid Dynamics is, ummm, computationally complex and the delays are just there to make sure the workload does eat up all the CPU time on the server.
Starting Testing
When writing new code, I always like to approach it in a Test-First manner. Because I won’t write solution code without a test that forces me to write it, I have a high confidence that the code is well covered with tests.
Unfortunately, dealing with legacy code means that the code is already written and the test-first approach won’t work. That’s ok, I have a little trick that I use. Just comment out the bodies of all the methods in the class you are about to test. Then write the tests that force you to uncomment the code. Just uncomment only enought to get the tests to pass, don’t uncomment anything you don’t have to. You have enough tests when all the code has been uncommented. The technique is almost as good as doing real test-first.
The Commented Out Version
Here is the code base as I started the test.
An Existence Test
I almost always start out with an existence test. Existence tests basically prove the proper files are included and the object can be created. Normally I delete these after a few tests have been written. But I left this one in for an example.
def test_initial_conditions
q = F3DQueue.new
assert_not_nil q
end
Nothing really exciting here. Let’s move on …
Proving FIFO Queue Order
The first thing I want to prove is that items put into the queue are removed in FIFO order. Since add_to_queue creates a AutoF3D object, I mock out the new method on the class object and tell FlexMock to expect new to be called twice. Once with :a, :b, and :c as parameters, then again with :x, :y, :z paramters. Each invocation of new will return a different symbol (:first and :second) so we can easily test the items are pulled off the queue in FIFO order.
Notice that I pass in simple symbols for the arguments to add_to_queue. Our code doesn’t interpret the values of the arguments, they are merely passed directly to the AutoF3D constructor. All we do is verify that the AutoF3D (mocked) constructor does indeed receive the arguments we pass in.
Here’s the test:
def test_adding_to_queue_is_removed_in_fifo_order
flexmock(AutoF3D).should_receive(:new).once.with(:a, :b, :c).and_return(:first).ordered
flexmock(AutoF3D).should_receive(:new).once.with(:x, :y, :z).and_return(:second).ordered
q = F3DQueue.new
q.add_to_queue(:a, :b, :c)
q.add_to_queue(:x, :y, :z)
assert_equal :first, q.remove_from_queue
assert_equal :second, q.remove_from_queue
end
This test caused three changes. First, the add_to_queue method needed lines uncommented:
def add_to_queue(modelLoc, params, gridFile)
autoF3D = AutoF3D.new(modelLoc, params, gridFile)
@queue.push autoF3D
# $log.info 'Request added to queue'
end
(Notice I didn’t uncomment the log. The logger is not needed to pass the test, and doesn’t contribute to the actual functionality of the method. I will not be testing the logger in the for the purposes of this article.)
Also the remove_from_queue needed its body uncommented:
def remove_from_queue
@queue.pop
end
And finally, the initializer code needed to create the queue array:
def initialize
@queue = []
# Thread.new{ process }
end
Notice that the Thread.new line is left commented. We will deal with that in a bit.
So now we run the test:
$ ruby test_f3dqueue.rb Started F. Finished in 0.010184 seconds. 1) Failure: test_adding_to_queue_is_removed_in_fifo_order(TestF3DQueue) [test_f3dqueue.rb:23]: <:first> expected but was <:second>. 2 tests, 2 assertions, 1 failures, 0 errors
Oops! This test uncovered the first bug. The code as written has stack behavior (i.e. LIFO). The naming seems to indicate that we want FIFO.
No problem. That’s an easy fix.
def remove_from_queue
@queue.shift
end
Now the tests run clean:
$ ruby test_f3dqueue.rb Started .. Finished in 0.001925 seconds. 2 tests, 3 assertions, 0 failures, 0 errors
Proving that Running a Job Works
Now when I run a job, I need to show that the proper four methods are called once each and in the proper order. This is very straight forward using FlexMock.
def test_running_a_job_will_call_the_right_stuff_in_the_right_order
job = flexmock("job")
job.should_receive(:generate_geometry_and_grid).once.ordered
job.should_receive(:partition_grid_and_initialize_flow).once.ordered
job.should_receive(:run_flow_solver).once.ordered
job.should_receive(:post_process_solution).once.ordered
q = F3DQueue.new
q.run(job)
end
Uncommenting the body of run is all that is needed here:
def run( job )
# $log.info 'Request being processed'
job.generate_geometry_and_grid
# $log.info 'Created Geometry'
job.partition_grid_and_initialize_flow
# $log.info 'Partitioned Grid'
job.run_flow_solver
# $log.info 'Flow Solver Completed'
job.post_process_solution
# $log.info 'Post process Completed'
# $log.info 'Request completed'
end
Test are now showing:
3 tests, 3 assertions, 0 failures, 0 errors
Processing an Empty Queue
Ok, now it gets interesting. I want to show that attempting to process a job when the queue is empty will cause the process to sleep for the check queue interval.
This is one spot where I changed the code to make it easier to test. It is difficult to test endless loops in unit tests (it tends to make the tests run a bit long), so I broke out the logic for a single pass through the loop into a method called process_one_job. We can then test this logic without dealing with the looping at the same time.
Note: It is possible to test endless loops and an example will be given below. But it is slightly tricky and this allows us to concentrate on proving the logic.
If there are no jobs to be processed, then all the code should do is sleep for a particular amount of time. We will locally mock out the sleep method on the queue object and insist that it will be called exactly once with the expected interval.
def test_processing_with_no_jobs_will_sleep_the_check_interval
q = F3DQueue.new
flexmock(q).should_receive(:sleep).once.with(F3DQueue::CHECK_QUEUE_INTERVAL)
q.process_one_job
end
Here is process_one_job with just two lines uncommented so that the test will pass.
def process_one_job
# execution_attempts = 0
job = remove_from_queue
# begin
# if job
# run job
# execution_attempts = 0
# sleep SERVER_RECOVERY_TIME
# else
sleep CHECK_QUEUE_INTERVAL
# end
# rescue
# $log.warn 'An error occurred during execution'
# $log.warn $ERROR_INFO
# $log.debug $ERROR_POSITION
# sleep SERVER_RECOVERY_TIME
# if execution_attempts > MAX_EXECUTION_ATTEMPTS
# $log.error 'Too many failed execution_attempts: aborting'
# raise
# else
# execution_attempts += 1
# retry
# end
# end
end
There’s a lot of code still left commented in that method. Now we need a test to force us to uncomment more code.
Handling a Single Job
Ok, now what happens when a single job is in the queue. We will assume the happy path (i.e. no exceptions) so we expect run to be called with the queued object, and then a sleep with the recovery interval.
A couple of things to note. First, we mock out AutoF3D again so that when we request something added to the queue, we control what kind of object is returned. We could return a mock object and then mock out the four methods that run will be calling.
However, I chose a slightly different approach. AutoF3D is mocked so that it returns a simple symbol. Then I mock out the run method to do nothing (but it is expected to be called once). This is slightly controversial because I am actually mocking a method on the object under test. But the run method is fairly simple, and we know that run works because of our previous test, so in the end we get clearer and simpler code.
Also note that the run and sleep methods mocks are ordered. This means run will be called first, then sleep.
def test_processing_with_a_single_job_will_run_the_job_and_pause_for_recovery
q = F3DQueue.new
flexmock(AutoF3D).should_receive(:new).once.and_return(:job)
flexmock(q).should_receive(:run).once.with(:job).ordered
flexmock(q).should_receive(:sleep).once.with(F3DQueue::SERVER_RECOVERY_TIME).ordered
q.add_to_queue(:a, :b, :c)
q.process_one_job
end
Now we get to uncomment even more lines in process_one_job.
def process_one_job
# execution_attempts = 0
job = remove_from_queue
# begin
if job
run job
# execution_attempts = 0
sleep SERVER_RECOVERY_TIME
else
sleep CHECK_QUEUE_INTERVAL
end
# rescue
# $log.warn 'An error occurred during execution'
# $log.warn $ERROR_INFO
# $log.debug $ERROR_POSITION
# sleep SERVER_RECOVERY_TIME
# if execution_attempts > MAX_EXECUTION_ATTEMPTS
# $log.error 'Too many failed execution_attempts: aborting'
# raise
# else
# execution_attempts += 1
# retry
# end
# end
end
That just leaves the error handling code to be uncommented. So that will be next.
Handling a Job With Errors
Now we want to test the case where processing a job will return an exception. This test exercise the exception recovery code in the original code base. The technique is similar to the last test, but this time we specify two mock calls for run. The first time run will return an exception. The second time it is called, it will complete normally.
Notice that we have ordered run and sleep so that they interleave execution with each other.
def test_if_a_job_fails_retry_after_recovery_time
q = F3DQueue.new
flexmock(AutoF3D).should_receive(:new).once.and_return(:job)
flexmock(q).should_receive(:run).once.with(:job).and_raise(RuntimeError).ordered
flexmock(q).should_receive(:sleep).once.with(F3DQueue::SERVER_RECOVERY_TIME).ordered
flexmock(q).should_receive(:run).once.with(:job).ordered
flexmock(q).should_receive(:sleep).once.with(F3DQueue::SERVER_RECOVERY_TIME).ordered
q.add_to_queue(:a, :b, :c)
q.process_one_job
end
I was showing this test code to one of my coworkers and they were a little surprised that the second expectation on run didn’t override the first expectation. FlexMock is explicitly designed to allow you to stack expectations like this. When searching for an expectation during mocking, FlexMock will use the first one matching one if finds. When an expectation has been used its designated number of times (in the above test, the once method designates that the expectation should only be used once), FlexMock will begin to use matching expectations that are defined later.
The upshot is this is that it is easy to define mock behavior for multiple calls to the same method.
Here’s the latest process_one_job method with some more lines uncommented. We are getting close to the end with this one.
def process_one_job
# execution_attempts = 0
job = remove_from_queue
begin
if job
run job
# execution_attempts = 0
sleep SERVER_RECOVERY_TIME
else
sleep CHECK_QUEUE_INTERVAL
end
rescue
# $log.warn 'An error occurred during execution'
# $log.warn $ERROR_INFO
# $log.debug $ERROR_POSITION
sleep SERVER_RECOVERY_TIME
# if execution_attempts > MAX_EXECUTION_ATTEMPTS
# $log.error 'Too many failed execution_attempts: aborting'
# raise
# else
# execution_attempts += 1
retry
# end
end
end
Processing Jobs that Continually Fail
Finally we test the case where the job will continually raise an exception until the error recovery code gives up and passes the exception on to the caller. I didn’t bother ordering the run/sleep calls here, making it easy to just specify that each are called four times. I believe that the previous test adequately specified interleaving.
I used a RuntimeError for my testing. If you have a specific error in mind, you might want to test explicitly for it. Generally raising the most general error you intend to handle is a good way of testing the boundry conditions on your rescue clause.
def test_too_many_failures_will_pass_along_exception
q = F3DQueue.new
flexmock(AutoF3D).should_receive(:new).once.and_return(:job)
flexmock(q).should_receive(:run).with(:job).and_raise(RuntimeError.new("XYZZY")).times(4)
flexmock(q).should_receive(:sleep).with(F3DQueue::SERVER_RECOVERY_TIME).times(4)
q.add_to_queue(:a, :b, :c)
ex = assert_raise RuntimeError do
q.process_one_job
end
assert_equal "XYZZY", ex.message
end
Note that the exception needs to be raised four times. I suspect this is a bug in the error handling logic. I left the logic as is and just made sure the test will pass. The code base specifies a retry count of “2”. This seems to imply that we try run twice, or perhaps three times (if the initail attempt doesn’t count as a retry). In any case, four times seems too much.
So, here is the code for process_one_job with most of its lines uncommented.
def process_one_job
execution_attempts = 0
job = remove_from_queue
begin
if job
run job
# execution_attempts = 0
sleep SERVER_RECOVERY_TIME
else
sleep CHECK_QUEUE_INTERVAL
end
rescue
# $log.warn 'An error occurred during execution'
# $log.warn $ERROR_INFO
# $log.debug $ERROR_POSITION
sleep SERVER_RECOVERY_TIME
if execution_attempts > MAX_EXECUTION_ATTEMPTS
# $log.error 'Too many failed execution_attempts: aborting'
raise
else
execution_attempts += 1
retry
end
end
end
Again note that this test surfaced a (rather minor) bug. There is an extra assignment that clears the execution attempt counter after a successful run of job. Since a successful run will exit the loop, clearing it has no effect (unless it is the sleep command that fails, that would be an interesting test scenario).
Since we haven’t shown the test results for a while, here’s how we stand at this point:
7 tests, 5 assertions, 0 failures, 0 errors
Processing Multiple Jobs
Now we know that we can handle a single job successfully. Now let’s make sure that we can handle multiple jobs. Remember that we broke process into two methods: process_one_job and a much shorter process that will call process_one_job in a loop.
Here’s what the original process method is looking like at the moment:
def process # loop do # end end
We pulled out its guts and left the still commented loop there. We haven’t even bothered to have it call process_one_job yet. So let’s write a test that will force us to fix that.
We will just mock out process_one_job so that it must be called 10 times. On the eleventh call it throws a symbol that we catch in the test. Throwing a symbol is the trick that breaks us out of the infinite loop. By throwing a symbol (rather than raising an error), we don’t interact with the error handling logic of the code under test.
This is actually the trick refereced earlier. By breaking the body of the loop into a separate method, we only have to use this trick once rather than on each of the process job tests.
def test_process_calls_process_one_job_in_a_loop
q = F3DQueue.new
flexmock(q).should_receive(:process_one_job).times(10)
flexmock(q).should_receive(:process_one_job).and_return { throw :done }
assert_throws(:done) do
q.process
end
end
To get this to pass, we implement the process method as follows:
def process
loop do
process_one_job
end
end
Threading Issues
Finally we need to make sure a thread is started. Here is another place I changed the code to make testing easier. The original code base started a thread in the initializer of the object. This means that every F3DQueue object ran in its own thread. This would means every test would have to deal with multithread issues. Yuck!
I changed the code so that a thread is started only when explicitly calling the start method. I like this better for real object anyways. Although it is an extra step, it gives you more control about when the threads are started. If you really want to start a thread at object creation, you can just say:
queue = F3DQueue.new.start
Since I really don’t want to start a Thread in the test (I just want to make sure that the Thread.new method is called), I mock out Thread.new so that it must be called once and when called will execute the given block.
I then mock out the process method to that it must be called once. The combination of these two mocks will ensure that start will start a new thread that calls process.
And finally, I ensure that the return value of start will be the queue object. This makes sure that the F3DQueue.new.start idiom works.
def test_start_will_start_a_process_thread
q = F3DQueue.new
flexmock("thread", Thread).should_receive(:new).with(Proc).once.and_return { |block| block.call }
flexmock(q).should_receive(:process).once
return_value = q.start
assert_equal q, return_value
end
And is is the little start method that needed to be written for the test. The Thread.new line is moved from the initialize method to here.
def start
Thread.new do process end
self
end
Here’s our final test run:
9 tests, 7 assertions, 0 failures, 0 errors
Code Coverage
We know that TDD gives pretty code code coverage stats out of the box. How did our “Comment-out First” approach do with regards to code coverage?
Here is the RCov report:
+----------------------------------------------------+-------+-------+--------+ | File | Lines | LOC | COV | +----------------------------------------------------+-------+-------+--------+ |AutoF3D.rb | 5 | 2 | 100.0% | |f3dqueue.rb | 82 | 53 | 100.0% | |test_f3dqueue.rb | 100 | 76 | 100.0% | +----------------------------------------------------+-------+-------+--------+ |Total | 187 | 131 | 100.0% | +----------------------------------------------------+-------+-------+--------+ 100.0% 3 file(s) 187 Lines 131 LOC
Wow! 100% on the first try.
Final Code Samples
You can find the final versions of the F3DQueue object and its tests here:
Future Directions
Now that the F3DQueue object is well testing, it is time to take a step back and think about the overall design of the class. There are a couple of things that stick out in my mind about this code.
(1) First Item
We did a lot of mocking on the F3DQueue object itself while it was being testing. Although a valid technique, you must be careful so that you don’t end up just testing your own mocks. What it does indicate is that the object you are testing might be trying to do too many things. Perhaps the class needs to be broken up into small classes, or perhaps some functionality needs to move into other classes.
With this in mind, the run method seems to know an awful lot about the workings of an Auto3D job object. It seems a bit out of place. Why don’t we move the run method to the job itself. Moving run into the Auto3D job object would allow us to write the following code fragment (in the process_one_job method):
...
job = remove_from_queue
begin
if job
job.run # was: run job
sleep SERVER_RECOVERY_TIME
...
Now, our queue class is one method shorter and is just concerned with the scheduling of the jobs and not the details of running the job itself. This is good …
Except for the following little piece of code, which leads us into the second thing that bothered me:
def add_to_queue(modelLoc, params, gridFile)
autoF3D = AutoF3D.new(modelLoc, params, gridFile)
@queue.push autoF3D
end
(2) Second Item
Here we have direct knowledge of the AutoF3D class. If we remove the reference to AutoF3D, then our queue will suddenly become much more general, and usable in situations where we might want to process a different kind of job.
I would recommend changing the above code to:
def add_to_queue(job)
@queue.push job
end
This does mean that adding a job to the queue would now have to create the job object explicitly. So, instead of:
queue.add_to_queue(loc, param, grid)
you would have to write:
queue.add_to_queue(new AutoF3D.new(loc, param, grid))
If you don’t like to manually create an AutoF3D object all the time (and I don’t), then the following solution is an easy fix to that:
queue = F3DQueue.new
def queue.add_job(loc, params, grid)
add_to_queue(AutoF3D.new(loc, params, grid))
end
The more traditionally minded of us might want to just subclass the F3DQueue class and add the add_job method in the subclass rather than in the singleton class. That works too. Either way, it is easy to do.
Recap
I hope this was useful for you. Here is a recap of some of the important ideas from this exercise:
- Comment-First is not a bad way to handle legacy code.
- Test scenarios, not methods. Note that I didn’t just pick a method in F3DQueue and write a single test for it. I choose scenarios that would exercise different sections of the code base. Start with the simple (e.g. a Job that Doesn’t Fail). Then pick increasing harder scenarios (e.g. “a Job that Fails Once”, “a Job that Fails Multiple Times”).
- Don’t be afraid to refactor to make testing easier. Breaking out process_one_job was a great idea that not only made testing much easier, but made the code easier to read.
- The “Use Symbols as Cheap Mocks” is an idea I stole from Stu Halloway in his “Refactoring of the Week” presentation. If a method takes arguments that you don’t want to deal with, try passing in symbols. If the arguments aren’t used, the symbols work great. If an argument is actually used, the error message will identify the symbol at fault. At that point, just replace the symbol with the appropriate mock. This technique save you lots of time and makes the tests easier to read.
- If you want to break out of an infinite loop in the code under test, throw a symbol from your mocks and catch it in your test. This generally doesn’t interfere with any exception handling code in your code under test.
- Always take a step back and look for ways of improving the code. A well tested module is fairly easy to change with confidence. Don’t be afraid to improve things.
More Samples
Do you have a bit of code that you are having trouble testing? If so, go ahead and send it to me. If your code is interesting enough, I’ll take a look at it and post the results here (so don’t send anything you aren’t willing to see published in this blog). I can’t look at everything, but I’ll try to find some interesting examples.
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furlFri 03 August, 2007

add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
Facebook API debugging
add to del.icio.us. look up in del.icio.us.
add to furlThu 02 August, 2007

2. Becoming active at twilight or before sunrise, as do bats and certain insects and birds.
add to del.icio.us. look up in del.icio.us.
add to furl
int main(){
pri
在edit mode下直接^p就应该提示printf,
如果cur在stdio.h中,则gf(goto file)应该直接跳转到stdio.h
but E447 在路径中找不到文件
然后在system path中加入D:/Program Files/Microsoft Visual Studio 8/VC/include
没用
重启
没反应
然后google,在.vimrc中加入
"set path+="D:\Program Files\Microsoft Visual Studio 8\VC\include"
还是不行
最后老老实实:help path
...(老老实实help)
最后如下,
set path+=D:/Program\\\ Files/Microsoft\\\ Visual\\\ Studio\\\ 8/VC/include/
set suffixesadd=.java,.py,.pyw,.h,.hpp
ok.
最后不忘:help!
别慌我!


add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
From the home page :
The Ruby Association LLC is an organization to help development of the programming language Ruby. The goals of the Ruby Association are to improve relationship between Ruby-related projects, communities and businesses, to address issues connected with using Ruby in an enterprise environment.
Seems to be Ruby Central, but for Japan.
It’s a bit hard to tell, so maybe I’m mistaken, but the logo looks like a Penrose Triangle .
And if it isn’t, then it should be.
add to del.icio.us. look up in del.icio.us.
add to furl
Better late than never, here are some thoughts from the erubycon conference in Columbus Ohio.
erubycon Themes
First of all, what a great conference. The talks were great and the hallway interactions were excellent. Glenn Vanderburg’s talk on “Enterprise, Schmenterprise” hit the nail on the head. (paraphrased soundbite: “The Enterprise it not ready for Ruby, it is desperate for it).
Testing, Testing, Testing
But the theme that kept coming back to me over and over again from the conference was testing: unit testing, integration testing, UI testing, all of them. Stu Halloway’s talk on the “Refactoring of the Week” emphasized the importance of tests to enable refactoring. In addition, Stu made a strong pitch for getting 100% code coverage in the projects you are working on. In selecting code to be refactored for his talks, he would just zero in on any code reported not covered by RCov and start looking there for fruitful refactoring possibilities.
So, the moral of the story is that if you have 100% code coverage, then Stu has to work a bit harder to find examples for his refacting talks.
I was inspired by Stu’s talk so I went back and checked all my open source projects to see how well they were covered by tests. I’m happy to say that both flexmock and builder are now at 100% and only needed a little tweeking to get that last percent or two. (Except for the CSS builder … we really need to finish that class or dump it).
The main library file of Rake is now at 100%. It was in the mid 90s when I check and needed some attention to get it the rest of the way. So that is good.
The unfortunate part is that there are some pieces of Rake that are not well covered. First there are a number of deprecated libraries that aren’t at 100%, and since people shouldn’t be using them, I’m more likely to remove them entirely than to write tests for them.
Second, the are some Rakefile tasks that are not adequately covered. I’m not sure how to address this for Rake tasks tend to be very involved in the environment you are working in, making it tedious to mock. I’d love to make the testing of Rake tasks easier, so fee free to make suggestions.
As for RubyGems … I’d rather not talk about the code coverage stats on that one.
So, I’m making RCov a part of my standard Rakefile setup and will start running it more religiously to keep those code coverage numbers up.
Emacs … We’re not dead yet!
I can’t really call it a “theme” of the conference, more like a strong undercurrent. There was certainly a number of programmers attending who use Emacs for their day to day editting, even when programs like TextMate are available. While emacs is a strong editor for a wide number of programming languages, its support for Ruby and Rails is lagging behind some of the more recent editors that are targetting Ruby specifically (e.g. the aforementioned TextMate). A group of us holdouts got together and shared some tips and tricks on bringing Emacs closer to the state of the art in Ruby support. I’ll share some of those tips here in the near future.
Next Time
Going into this conference, I heard the organizers swear they were never doing this again, but the the end everyone was enthusiastic about next year. So, who knows, if you’ve missed this year’s erubycon, you might get a chance to join us next year.
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furlWed 01 August, 2007

From westciv :
XRAY is a bookmarklet for Safari, Firefox, Camino or Mozilla. Use it to see the box model for any element.
And it is. When loaded, it will allow you to click on any element in the current page and show you all sorts of creamy CSS goodness.
Very groovy
add to del.icio.us. look up in del.icio.us.
add to furl
If you can get past Hitchens' polemic against all religion, this commentary is excellent.
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
目录
(一)系统环境:
- Ruby 语言 1.8.4版本, 点击这里Ruby1.8.4。要想安装多个Ruby语言运行环境请看这里。
- Rails 框架 1.2.1版本,安装方法请看这里,最简单方法是第一种方法即可。
- Windows XP 或者 Windows 2000操作系统 或者 Linux操作系统
- 需要一个浏览器,如FireFox1.5.0.1以上版本。
- 开发编辑工具 Notepad2 ,安装方法请单击这里,复制一个notepad2.exe,并且更名为vi.exe。
- 在Windows XP上安装Linux核心命令,点击这里。
- 如何在Windows Console下使用命令svn(下载软件),点击这里。
(二)前提条件:
- 在本机Winodw操作系统上,我们的工作目录为d:\works_rails。
- 你的电脑必须在线。
(三)目的:
- RailsMyAdmin是一个Rails框架软件的插件,它可以管理你的Rails框架软件数据库,注意不仅仅可以是MySQL数据库,也可以是其它的数据库。
- 本讲座的完整代码请您在Google Code Hosting上查看:
或者下载https://cnruby.googlecode.com/svn/trunk/rails-projects/use_railsmyadminsvn co https://cnruby.googlecode.com/svn/trunk/rails-projects/use_railsmyadmin
(四)解决方案:
- 创建一个Rails框架的应用软件,切换到软件的根目录。
rails use_railsmyadmin
cd use_railsmyadmin - 修改数据库配置文件
vi config/database.yml【具体内容如下:用下面的代码替换原文件的所有内容】development:
adapter: mysql
database: myadmin
username: root
password: root
host: localhost
test:
development
production:
development - 创建一个MySQL数据库myadmin
mysqladmin -u root -proot create myadmin - 自动生成一个基于模型Admin的相关代码,增加表admins的字段username和password,最后执行生成数据库表的命令。
ruby script/generate scaffold_resource admin
vi db/migrate/001_create_admins.rb【具体内容如下:放在create_table一行下面】t.column :username, :string
t.column :password, :string
rake db:migrate - 第一步:安装RailsMyAdmin插件;第二步:生成应用于我们软件的代码;第三步:修改我们软件的环境配置文件。
ruby script/plugin install http://railsmyadmin.googlecode.com/svn/trunk/my_admin/
要是第一次安装失败,再使用下面命令执行。
ruby script/plugin install --force http://railsmyadmin.googlecode.com/svn/trunk/my_admin/
ruby script/generate my_admin
vi config/environment.rb【具体内容如下:放在该文件最后】## MY ADMIN CONFIG BEGIN
require 'my_admin/my_admin_tool'
# If you only want certain models to be available to RailsMyAdmin,
# set :all_models to false and specify the desired models in MY_ADMIN_MODELS
MY_ADMIN_GLOBALS = {:all_models => true, :confirm_destroy => false}
# Uncomment the following line if you set :all_models to false above.
#MY_ADMIN_MODELS = [User, Content]
# Replace [User, Content] with your desired array of model classes that
# RailsMyAdmin should be restricted to.
# MY_ADMIN_AUTH must define a Proc object that takes as a paramater
# an ApplicationController instance variable (c - in the example below).
# If you have a method defined in your ApplicationController,
# 'admin_logged_in?' for example, the following sample code will
# authenticate against that method and only allow visitors to
# view RailsMyAdmin if the 'admin_logged_in?' method returns true.
MY_ADMIN_AUTH = Proc.new { |c| c.send('admin_logged_in?') }
## MY ADMIN CONFIG END - 启动网络服务器
ruby script/server - 再打开一个Shell,执行下面命令
start http://localhost:3000/my_admin/main - 我们看到该插件结果图:

(五)视听教学:
(六)必须注意的问题:
(七)参考资料:
(八)命令清单:
rails use_railsmyadmin
cd use_railsmyadmin
vi config/database.yml
development:
adapter: mysql
database: myadmin
username: root
password: root
host: localhost
test:
development
production:
development
mysqladmin -u root -proot create myadmin
ruby script/generate scaffold_resource admin
vi db/migrate/001_create_admins.rb
t.column :username, :string
t.column :password, :string
rake db:migrate
ruby script/plugin install http://railsmyadmin.googlecode.com/svn/trunk/my_admin/
ruby script/plugin install --force http://railsmyadmin.googlecode.com/svn/trunk/my_admin/
ruby script/generate my_admin
vi config/environment.rb
## MY ADMIN CONFIG BEGIN
require 'my_admin/my_admin_tool'
# If you only want certain models to be available to RailsMyAdmin,
# set :all_models to false and specify the desired models in MY_ADMIN_MODELS
MY_ADMIN_GLOBALS = {:all_models => true, :confirm_destroy => false}
# Uncomment the following line if you set :all_models to false above.
#MY_ADMIN_MODELS = [User, Content]
# Replace [User, Content] with your desired array of model classes that
# RailsMyAdmin should be restricted to.
# MY_ADMIN_AUTH must define a Proc object that takes as a paramater
# an ApplicationController instance variable (c - in the example below).
# If you have a method defined in your ApplicationController,
# 'admin_logged_in?' for example, the following sample code will
# authenticate against that method and only allow visitors to
# view RailsMyAdmin if the 'admin_logged_in?' method returns true.
MY_ADMIN_AUTH = Proc.new { |c| c.send('admin_logged_in?') }
## MY ADMIN CONFIG END
ruby script/server
start http://localhost:3000/my_admin/main
(九)下载文件pdf:
add to del.icio.us. look up in del.icio.us.
add to furlTue 31 July, 2007

add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furl
Condensing thoughts down to one sentence …
In One Sentence …
So I was asked this question in IM today:
If you had one sentence to explain to a Java programmer why Dependency Injection is rarely necessary in Ruby, what would it be?
Wow, one sentence! After some thought, here’s what I sent back:
Dependency injection provides vital flexibility in Java and unneeded overhead in Ruby.
Anyone have other suggestions?
add to del.icio.us. look up in del.icio.us.
add to furl
RailsConf Europe is approaching fast. It’s going down September 17-19 in Berlin, as most of you know, but this very week is actually the last if you want to take advantage of the 150 euro-discount for early-bird registrations.
I can’t wait to see everyone in Berlin. We had such a great conference in London last year and so much has happened since then. Have a look at the program and hopefully we’ll get a chance to meet up and talk.
add to del.icio.us. look up in del.icio.us.
add to furl
We’ve passed the 3-year mark for when Rails first saw the light of day as a public release. It’s been an amazing ride, don’t you think?
add to del.icio.us. look up in del.icio.us.
add to furl
We've just passed the three year anniversary of Ruby on Rails' first release to the public. Which in turn means that I've been working on the framework for more than four years. Wild.
It's been an incredibly rewarding experience. I've met so many great people. Worked with so many talented programmers. Seen so many amazing sites and applications be launched off Rails.
To image that this whirlwind tour all started because I wasn't happy working with the mainstream environments and decided to give Ruby a try. And to image the impact Ruby on Rails have had on the industry with none of the big-company backing that traditionally is needed to make waves there.
What's even more rewarding is knowing that we're not done. That the work to make web-application development ever more pleasant continues every day.
We might have taken the pace of radical changes in the core framework down a few notches (remember when we did multiple releases per month?), but the steady stream of improvement and refinement continues relentlessly.
So cheers, Rails.
add to del.icio.us. look up in del.icio.us.
add to furl
“ There is abundant evidence to show that high buildings make people crazy. ”
Christopher Alexander in A Pattern Language
add to del.icio.us. look up in del.icio.us.
add to furl
add to del.icio.us. look up in del.icio.us.
add to furlMon 30 July, 2007

JRuby邮件列表的讨论
http://www.nabble.com/Moving-to-Java-5--tf4131923.html
InfoQ报道
http://www.infoq.com/news/2007/07/jruby-java5-move
http://www.infoq.com/cn/news/2007/07/jruby-java5-move
XRuby起步就是从Java 5开始的,所以,不存在这个问题。在他们还在为此争论的时候,受到Charles Nutter最开始那封邮件的启发,我已经完成了Annotation标记Java代码和Ruby代码绑定的第一个版本。
下面是一个例子:
@RubyLevelClass(name="ClassFactory")
public class ClassFactoryValue extends RubyValue {
...
@RubyLevelMethod(name="test", type=MethodType.NO_ARG)
public RubyValue test() {
return RubyConstant.QNIL;
}
}
首先,用@RubyLevelClass标记出这个Java类对应着一个Ruby层次上的类,其名称为ClassFactory。然后,用@RubyLevelMethod标记出一个Java方法是对应着Ruby的方法,它的Ruby名称为test,而且它是无参数的。
我们可以这样利用这段代码:
RubyClass klass = RubyTypeFactory.getClass(ClassFactoryValue.class);
通过RubyTypeFactory,我们可以生成ClassFactoryValue将Java层次和Ruby层次对应起来的代码,生成代码大致如下:
public class ClassFactoryValue$ClassBuilder implements RubyClassBuilder {
public RubyClass createRubyClass() {
RubyClass rubyclass = RubyAPI.defineClass("ClassFactory", RubyRuntime.ObjectClass);
MethodFactory methodfactory = MethodFactory.createMethodFactory(ClassFactoryValue.class);
rubyclass.defineMethod("test", methodfactory.getMethod("test", MethodType.NO_ARG));
return rubyclass;
}
}
这里用之前介绍过的生成方法Wrapper的MethodFactory去辅助代码生成,简化了编写。
XRuby本身为了生成bytecode已经做了大量的代码生成,这里只是把代码生成更多的用在了其他的部分。把这里的Annotation更广泛的用在XRuby中,会让代码看上去更干净。
add to del.icio.us. look up in del.icio.us.
add to furl


