Posted by Jeremy Voorhis
Thu, 13 Jul 2006 22:09:00 GMT
This morning, I refreshed NetNewsWire1 to see the headline Rails: Not a DSL. After clicking on the feedreader’s link, which linked to Artima, which linked to DZone, which linked to Digg, which linked to the blog with said article.
The message of the article boiled down to something like this: you can’t call everything a DSL. Sure. If you repeat any word to yourself enough times, it loses its meaning2. Well, it’s true. Rails’s expresiveness is a matter of Ruby being used the way it was meant to be used. It’s how your application and your platform interface – if you are taking advantage of a dynamic language, the two may then intertwingle and appear as an arch.
For an exampe of this style of application design, I can make Ruby’s
SHA1 implementation – a method that I use frequently for password encryption and for generating unique tokens – available directly from a String object as follows.
class String
# Returns the SHA1 digest of a +String+.
def to_sha1
Digest::SHA1.hexdigest self
end
end
and call that code as follows:
password_hash = password.to_sha1 unless password_hash
This may be called throughout the application and it is easy to read. I have not, however, created an internal
DSL for string encryption. If you need a label for this style of application design, call it a
fluent interface, or the
arch. Or just call it good design.
Finally, what separates a DSL from just good design? While I cannot provide exact criteria, I think the answer lies in the motivation for which the language-oriented tool in question was created. More specifically, was it created to solve a specific problem with a narrow scope? Was it also designed to share meaning with an expert?
An excellent example of a DSL is the Spirit parser library, which allows C++ developers to transcribe a grammar, likely created by a language or protocol expert, into executable code. While its feats are much simpler, I believe the asset compiler library I have written also qualifies as a domain-specific language because it was created to be a language-based tool that eased communication between my client – a domain expert in image processing – and my application, transcribing their requirements almost directly into Ruby. It is worth noting that my work rarely involves creating or even using DSLs; they are just another tool at my disposal, especially when solving a specific problem that requires communication with experts.
1 Yes, it is still my mainstay feedreader.
2 In my experience, the word “each” is very susceptible.
2 comments
Posted by Jeremy Voorhis
Mon, 10 Jul 2006 22:17:00 GMT
My morning routine includes catching up on a few feeds, including Ongoing and Intertwingly. After skimming Ruby’s followup to Tim Bray’s first post about Camping, I clicked the headline link in NetNewsWire only to arrive at a dead-end. The problem?

Curious as to how Sam Ruby, champion of the Atom publishing protocol and syndication format, could have such a jaggy feed, I checked the source and was met with a wad of SVG. I cross-checked the results in NewsFire and it generated an even more bizarre URL, beginning with applewebdata:// followed by a GUID.
Although neither NewsFire nor NetNewsWire claim SVG support at this time, I’m disappointed by the lack of robustness. One of the most interesting facets of Atom and RSS is how they take full advantage of XML by allowing you to mix in markup from another namespace.
3 comments
Posted by Jeremy Voorhis
Mon, 10 Jul 2006 08:34:00 GMT
Maybe I missed something, but today I was looking for a generic mocking framework for Ruby that would allow me to create a mock class, not just a mock instance. I almost achieved this with Jim Weirich’s excellent FlexMock, and decided not to try out Test::Unit::Mock or SchMock just yet. Instead, I ended up writing the code at the end of this post. If you have a favorite mocking tool that I haven’t mentioned, or know how to mock classes with an existing tool, please post a comment!
Here is the code for SimpleMock:
module SimpleMock
module Recorder
def messages_received
@messages_received ||= []
end
def method_missing message, *args, &block
messages_received << { :message => message.to_s, :arguments => args }
end
def message_received? message, *args
message = message.to_s
!messages_received.select { |m| m[:message] message && m[:arguments] args }.empty?
end
end
class Base
include Recorder
extend Recorder
end
module Assertions
def assert_message_received mock, message, args
error_message = ”#{mock}.#{message}(#{args.join(’,’)}) expected but not received.\n” +
”#{mock} received the following messages: \n #{mock.messages_received.inspect}”
assert mock.message_received?(message, args), error_message
end
end
end
Here is how it is used:
class Entry < SimpleMock::Base; end
class EntriesController < ActionController::Base
include RapidResource::Crud
# Re-raise errors caught by the controller.
def rescue_action(e) raise e end
end
class RapidResourceTest < Test::Unit::TestCase
include SimpleMock::Assertions
def setup
@controller = EntriesController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
def test_find_member_should_find_entry_by_id
get :show, :id => 7
assert_message_received Entry, :find, 7
end
end
1 comment
Posted by Jeremy Voorhis
Mon, 10 Jul 2006 01:32:00 GMT
Today, Brian Ford and I evaluated NewsFire. I had heard about NewsFire from Phill Ryu’s post about 10 beautiful OS X apps and noticed NewsFire ranking high on the list. Visually, NewsFire certainly earns its title. Its two-pane view is easier and faster to use than NetNewsWire’s three-pane view inherited from Outlook. The two panes afford a much larger viewing area, which solves my frustration with NetNewsWire’s summary pane; I resize the pane every time I start the app, but it never remembers my preference.
My biggest frustrations with NewsFire so far are related to its OPML import. If you do not buy a license, and I have not just yet, NewsFire only allows you to subscribe to 15 feeds. This is fine for evaluating, but I did not know the limit and I attempted to import a 45-feed OPML file I exported from NetNewsWire. First, NewsFire ignored the OPML groups, so I will be forced to organize my feeds again if I make the switch. It also gave me a modal dialog with only one path forward: register NewsFire. I helplessly clicked the register button so I could move forward, but the application promptly crashed. NewsFire loses points for its sloppy shareware behavior, but the application is a joy to use otherwise.
8 comments
Posted by Jeremy Voorhis
Thu, 06 Jul 2006 21:43:00 GMT
At PLANET ARGON, we typically do not keep our database.yml files in subversion, leaving behind a database.yml.example file instead. This makes it easy for us to share the repository with multiple developers who work on their own conventions, or sometimes an alternate database engine. We still want to have a one-step automated deployment process, however, and ssh-ing into the server to create database.yml manually just feels a little… uncouth.
The solution? Building upon Tim’s post at “http://toolmantim.com/article/2006/5/26/setting_up_capistrano_on_segpub”, our Capistrano deployment recipe now writes database.yml after we deploy, and symlinks it to the latest release directory after we update the code. Here is the code that lets us do that:
desc "Create database.yml in shared/config"
task :after_setup do
database_configuration = render :template => <<-EOF
login: &login
adapter: postgresql
host: localhost
port: <%= postgresql_port %>
username: <%= user %>
password: <%= password %>
development:
database: <%= "#{application}_development" %>
<<: *login
test:
database: <%= "#{application}_test" %>
<<: *login
production:
database: <%= "#{application}_production" %>
<<: *login
EOF
run "mkdir -p #{deploy_to}/#{shared_dir}/config"
put database_configuration, "#{deploy_to}/#{shared_dir}/config/database.yml"
end
desc "Link in the production database.yml"
task :after_update_code do
run "ln -nfs #{deploy_to}/#{shared_dir}/config/database.yml #{release_path}/config/database.yml"
end
UPDATE
I’ve revised the code on 10 June. Use this version.
3 comments
Posted by Jeremy Voorhis
Fri, 30 Jun 2006 07:18:21 GMT
This is just a quick warning to anybody who may be using custom rules with the Rails inflector.
In a personal project I am tooling around with, I define the following custom rule for the inflector:
Inflector.inflections do |inflect|
inflect.uncountable 'trash'
end
but a named route defined by the simply_restful plugin gets the helper trashes_url. Of course, this makes perfect sense – by the time your app is loading your custom inflections, your plugins are already available to be used. simply_restful had pluralized my uncountable word and defined my named routes before I ever had a chance to object.
The interesting question here is which behavior is correct?
2 comments
Posted by Jeremy Voorhis
Wed, 28 Jun 2006 03:14:00 GMT
Here’s a follow-up story about PLANET ARGON’s asset compiler project in action.
In our client’s application, an Image model is created for each image they upload and the uploaded images are saved into an asset source directory. After the images are saved, an observer launches the build system, acting on any source images whose targets are missing.
While I was speaking at Railsconf, a client of ours posted a bug to our Basecamp. For reasons I am still trying to determine, our client had uploaded four images to their application but the asset compiler task was not executed. It might take a minute to find the disconnect between my application and Rake, but our client needs those images that coincide with a press release as soon as we can deliver them.
Because asset compiler is build on top of Rake, it receives all of the benefits of Rake. I recalled that Rake supported a way to perform a dry run of a task execution (actually the option is called
--dry-run). Because each individual target file is a dependency of the top level task,
assets:build, running
rake assets:build --dry-run
tells me exactly which target files were missing. Sure enough, they coincided perfectly with the files the client reported were missing. By running
rake assets:build --dry-run | grep "** Execute"
I can ignore any file tasks which are not necessary. Within five minutes, I was able to diagnose the problem and allow my client to proceed after building the remaining target images and uploading them to the bandwidth provider, which was as simple as running
rake assets:build
The beautiful thing here is how easy asset compiler lets me manage 4000 images. The application knows when to run build tasks, and in my assets.rake file, I can concisely define 11 types of image transformations. I can run each type of transformation individually, or I can run them in one shot. Even better, since rebuilding all of the images takes considerable CPU power, I can rsync my asset source directory to a separate build server and run the build tasks there, exporting them to our web server and the bandwidth provider.
For those of you unfamiliar, the asset_compiler project is hosted at planetargon.org and there is an article on InfoQ that tells the story of its motivation and implementation. Asset compiler is free software.
UPDATE
Here is the amazingly helpful output I got from
rake assets:build --dry-run | grep "** Execute".
** Execute (dry run) assets:navigation_icon:build
** Execute (dry run) assets:bronze_navigation_icon:build
** Execute (dry run) assets:navigation_icon_no_resize:build
** Execute (dry run) assets:bronze_navigation_icon_no_resize:build
** Execute (dry run) /path/to/assets/full_size/Ronaldo9mVert1.jpg
** Execute (dry run) /path/to/assets/assets/full_size/Ronaldo9mHoriz2.jpg
** Execute (dry run) /path/to/assets/assets/full_size/Ronaldo9mVert2.jpg
** Execute (dry run) /path/to/assets/assets/full_size/Ronaldo9mHoriz1.jpg
** Execute (dry run) assets:full_size:build
<snip />
** Execute (dry run) assets:build
This is exactly the kind of clear and unambiguous feedback I like to see when investigating this kind of problem. A big thank you to Jim Weirich for his thoughtful implementation of Rake.
Posted in Rails, web development, PLANET ARGON, Architecture | no comments
Posted by Jeremy Voorhis
Tue, 27 Jun 2006 22:00:00 GMT
I just read Jason Watkins’ followup to Jim Greer’s post about CRUD and I think Jason’s claim that simplicity is lost is dubious. State-changing methods like Ticket.close! give us a simple interface to send a command to a domain object, but they may be hiding any amount of complexity. When we introduce the TicketClosure, we let our graph of model instances tell us directly based solely on their existence. That is what enables domain operations to be reduced to simple CRUD, where each of the four primary verbs are universally understood.
Also, if we step back one level, we are reminded that a verb is also a noun on paper – we talk about performing commands on our primary domain models, but operations, actions, etc. nouns. What we are talking about is journaling state changes on entities within our domain model. If you are unfamiliar with the command pattern, I encourage you to read the Wikipedia entry about the command pattern for an excellent treatment of this design pattern.
Using Jason’s Ticket example, let’s imagine that TicketClosure inherits from TicketCommand. TicketAssignment and TicketPriorityChange could also inherit from TicketCommand, which would expose simple operations such as reassigning a ticket to another developer, etc. The ticket’s state is now a composition of the commands it has received. These commands all expose their own simple CRUD operations, and our Ticket model now supports undo operations by manipulating its journal of commands with simple CRUD.
What other uses can you envision for the command pattern as an integral part of a domain? Any criticisms?
5 comments