But in 2007, you do need to let go of the idea that all we're doing with “business programming” is building web applications that are replacing the client-server applications of the eighties and nineties that themselves replaced the green screen terminal applications of the seventies and eighties.The “leading edge” interface and ideas employed by Google, Amazon, eBay, and Yahoo! are suffusing our culture to become the standard user interface of web applications. And programming the standard user interface is a basic job requirement. Learn to love it.
The numbers: 3, 7, 10, 2038, 32767, 65535, -9, 1.7, and 0. These are numbers I like to test with. There's a reason I use each of these numbers. Each number has a story of its own.
Well worth the reading.
Perhaps it's why dynlang programming starts to feel so right...it's putting trust and control back in our hands.It does "feel so right"….You might say that dyntyped languages trust the programmer to do the right thing where static typed languages force the programmer to do the right thing. That trust opens up a world of possibilities, but it also takes some responsibility out of the computer's hands—namely, responsibility for exactly that information that's needed to refactor.
What's the worst-case scenario? Well, I used to have this friend who worked at a tech company we'll call "FooCorp". This is a true story: at FooCorp, the HR department decided to hire a lone cowboy programmer to write a new performance-evaluation tool for the company. The programmer they hired—who had no interaction with the rest of engineering—went ahead and decided to write it in Ruby. With much ado and fanfare, they rolled it out, and with even more ado, a thousand people spent hours entering in their performance reviews. And then, with a truly enormous amount of ado—I mean enough ado to fill several football stadiums—the shiny new Ruby-based perf tool went on a rampage and ate up everyone's performance reviews. Forever.
Was that Ruby's fault? Of course not. In fact I, er, that is, my friend met with the cowboy afterwards to figure out whether the disaster could be undone, and the cowboy announced: “WELL, YOU KNOW, WHEN THE SYSTEM GETS ENUFF CHAOS GOIN' IN THERE, PRETTY MUCH ANYTHING COULD GO WRONG.” And I can't say he was wrong, either.
Needless to say, Ruby got a bad rap at FooCorp for a little while.
Sorry. I just liked the story.
Sam found this approach “very useful in arriving at a solution”:Ryan's response: “yeah. in early Christopher Alexander-speak, that process pulls the forces apart so you can see the dependencies…you can evaluate all the forces that push on the problem and solution.”
- write down the problem
- write down why it's hard to solve
- write down what would make it easier
- write down possible ways to implement things that would make it easier
- write down why those ways suck
- then make it not suck
The best qualities of high- and low-level languagesA well-written program in an abstract language like Ocaml or Lisp has the quality of an elegant mathematical proof: beautiful and concise, but you can't change anything without breaking it. Most programs in C++ are more like an elaborate model train layout, supporting endless tinkering without actually stopping the train from going 'round.
Of course, these days the tinkering can be more intentional, and we call it “refactoring” (“I'm sorry, I can't attend that meeting, this code has some bad smells, and really needs refactoring” might buy you more time than, “Sorry, I'd rather tinker with this old program; have a good meeting.”).
Since I started writing in Ruby (and also, less skillfully, in Python), I've had much less tinkering to do: getting some things done in Java, or in (old-style: I haven't worked a lot with the C++ STL, so things may be different) C++ just needed a lot more little helper classes here and there to sort or iterate or whatever, and they could all use a little polish and dusting from time to time.
On the other hand, there always seems to be more to do, so turning out fewer lines of code for any one task is a good thing, and lingering over it (when it does what it's supposed to and is relatively clear) just doesn't appeal as much as it used to.
Therefore, I took advantage of the flexibility of Ruby to create a list_for class method, below:
class ApplicationController < ActionController::Base
# Generate a controller method to list _children_ (of this class) for
# a given _parent_ of some other class.
# @parent@:: name of parent entity, as string
# or symbol: @:community@
# @order_clause@:: order for listed children: defaults to 'id ASC'
# @children@:: plural name for collection passed to 'list' view.
# Will default to the prefix of the controller name,
# thus, foo_bars_controller will
# yield a collection named @foo_bars. For
# that matter, product_controller will yield
# a collection named @products, because
# we pluralize the name anyway.
#
def self.list_for(parent,order_clause='id ASC',children=nil)
unless children
children = self.name.underscore.gsub(/_controller$/,'').pluralize
end
parvar = parent.to_s.underscore
parcls = parent.to_s.camelize.constantize
child_qual = children.to_s.singularize.underscore
child_var = children.to_s
pages = "@#{child_qual}_pages"
code = %Q{
def list_for_#{parvar}
#{parvar} = #{parcls}.find(params[:id])
#{pages} = Paginator.new self,
#{parvar}.#{children}.count, 10, @params['page']
@#{children} = #{parvar}.#{children}.find(:all,
:order => '#{order_clause}',
:limit => #{pages}.items_per_page,
:offset => #{pages}.current.offset)
@title = "#{children.humanize} for #{parvar.humanize} '\#{#{parvar}}'"
render :action => :list
end
}
module_eval code
end
end
So, if in my locations_controller.rb file I have the line
... list_for :community, 'Service_Location ASC' ...the class method will generate a list_for_community method that expects the id parameter to identify a community. It will then pass to the default 'list' view an instance variable @locations (it takes the name from the controller class) which contains only the locations for the selected community.
But once you've admitted that [Perl 5 is more powerful than Perl 4], you've admitted that one high level language can be more powerful than another. And it follows inexorably that, except in special cases, you ought to use the most powerful you can get.Maybe always looking for a new language is a good idea.This idea is rarely followed to its conclusion, though. After a certain age, programmers rarely switch languages voluntarily. Whatever language people happen to be used to, they tend to consider just good enough.
Update:I fixed a typo above.
Herewith, an overview of the new testing stuff, a how-to for upgrading your existing tests (though you don't have to), an obligatory math formula or two, and a performance comparison that's worth about as much as you paid to read this.So I gave it a try...and was more than pleasantly surprised: the total test time was reduced (in one set of runs) from seven minutes down to about 40 seconds: the optimized tests run in just about 10% of the original test time.
The following table shows the run times (in seconds) for my Culinary Census test suite. There are maybe 260 to 300 rows of data generated by the fixtures in 15 or so tables. Some 250 rows are static, the rest dynamically generated.
| Type of Test | No Optimization | Non-instantiated | Transactional | NI and Transactional |
|---|---|---|---|---|
| Unit Tests 96 tests, 474 assertions |
370 | 86 | 325 | 35 |
| Functional Tests 47 tests, 170 assertions |
60 | 15 | 47 | 6 |
| Total | 430 | 101 | 372 | 41 |
| Use Transactional Fixtures |
Use Instantiated Fixtures |
|
|---|---|---|
| No Optimization | false | true |
| Non-instantiated | false | false |
| Transactional | true | true |
| NI and Transactional |
true | false |