I wrote some thoughts on Ruby on Rails a couple months ago. But
that was just formed by reading a bunch of docs, which is actually
about as far as I get into a number of things. I'm a little
embarrassed to admit it, but I often build strong opinions on things
I've only read about. But, I don't really feed bad about it; I try
not to be a curmudgeon and I give projects the benefit of the doubt.
Installation sometimes feels like a waste of time when I just want to
look around.
But anyway, since I've been thinking about Rails lately, I thought
I'd give it an actual test run. I only went through the quite-short
Making a to-do list
tutorial.
So, I installed Ruby from the Debian packages (0.11.1) and set out.
Impressions...
First thing that Rails does is create a bunch of files and directories
for your project. I think this is a good idea, and something I added
recently to Paste. (When I was introducing a coworker to Webware,
the lack of a clear convention for file structure was actually one of
his first concerns.)
Now... the actual files. More than I would have expected -- 11
directories at the top level, and 29 total. I'm not a fan of
hierarchy, so this puts me off a little. And it feels more complex
than I expected. But people can argue about file layout forever.
Onward... I made a controller. I notice controllers feel a lot like a
published object, or even Webware's servlets. Controllers are
classes, and every method is its own page/URL (index as the
default all the way down). There's other more complicated ways to map
URLs, but personally I find URLs a little boring. People who start
applications with URL design are, IMHO, displaying classic engineer
tendencies wherein they focus on small technical details that don't
matter in any larger picture. But I digress...
I find it odd that every method is published. Which means there's
no private methods. Instead there's a "helper" class (for
app/controllers/todo_controller.rb there's
app/helpers/todo_helper.rb). That's... awkward. At one time Zope
used docstrings to mark methods as public, but that was a bad idea
("docstring abuse"). Now (in Python) function attributes are an easy
way to do that, like:
def screen_method(...): ...
screen_method.public = True
Or decorators can be used to the same effect, but with somewhat more
pleasant syntax. Or you can just use a naming convention (a name
prefix that indicates a method is public). But in Rails not only do
helper methods not go in the same class, or the same file, or even the
same directory, but in a separate file in a sibling directory.
Strange. I think there's also a good deal of mix-ins happening -- not
just the helper into the controller, but the template into the
controller as well. Mix-ins were something people thought to be a
good idea in Python some
time back, but I think we've all realized they don't scale well (no
worse than multiple inheritance, but that doesn't scale well either).
Or at least mix-ins implies modularity where in fact there is none.
Lots of things get passed around as instance variables of the
controller. It's encouraged (from what I can tell) to use instance
variables for even local scope, so that helper methods can find these
variables by name. People who study language history will know
pass-by-name as the black sheep in the pass-by-value/pass-by-reference
debate. No one uses pass-by-name anymore. (Well, maybe that's not
entirely true -- Tcl uses it for arrays, PHP uses it for callbacks,
REST uses it for identifiers, and everyone who uses XML configuration
uses it for all sorts of stuff.) I'm sure there's good reasons for
this stuff, but I also feel like Rails is willing to compromise on
predictable isolated programming constructs if it saves them a line or
two of code. I like short code, but I like locally-understandable
code much more.
The model thing confuses me a bit as well. I'm used to models being
an abstract concept. Models are your objects. Yours, not the
framework's. The model can be a dictionary, or a number, or a ORM
object, or a set of functions that call to a database procedurally, or
code that queries remote services. Whatever. I don't see much of a
difference between your "model" and your "library". But Rails
formalizes it -- at least a little -- but making a separate directory
for models. Presumably they are distinguished in other ways too.
It's hard to tell the difference between suggestive convention --
meant to embody good practices -- and meaningful distinctions.
One thing I like about the generation process (generating models,
controllers, etc) is that it also extends the test framework. That's
definitely something that is going to go into Paste. In addition to
adding files for unit tests, it also adds model fixtures, which are
some sample data. Stuff you could use to see what the website looks
like in a more realistic situation, or a way to avoid some tedium in
your unit test setup. This is similar to what scaffolding provides --
not a great UI, but just enough stuff to give you enough navigation to
get to the part you are working on right now. I think there's value
in that, and the name indicates its purpose, but it's also easy to
over-appreciate because it seems magic.
One last thing that struck me was the templates. In a bad way. The
templates are a standard ASP-style of template; a classification one
commenter seemed offended by in my last post, but it's not meant that
way. That's just what they are; they look exactly like ASP. So what?
Anyway, I don't care much about that. I think ZPT
has some interesting ideas about how to put code into HTML, but I've
never been that convinced that tag-based templates are easier or even
as easy as more textual/program-oriented templating languages.
What bothered me was the way they dealt with reuse. At least in the
template two options were given -- one generated the HTML in plain
Ruby code. Clearly ugly. The other use "partials", which a way of
saying "a second template that gets embedded".
Well, like I said, I don't like hierarchy, and similarly I like
keeping information together. As a result, I really like templating
languages that have structure. Both ZPT and Cheetah have this,
though using different metaphors. In ZPT you have macros (METAL), and
you use this for both the site wrapper and for reusing markup inside a
page. It's very nice; it really is a macro, and I'm not a big fan of
macros, but I haven't had any problems with that aspect. You can put
lots of macros in a single file, you can give them all sorts of
internal structure, etc. Works great. Cheetah does it with #def
and representing templates as classes. This serves many of the same
purposes -- the ZPT macro is a Cheetah abstract superclass, and the
macro "slots" are methods in Cheetah. Either way you can define rich
structure without adding new files or putting in new conditionals and
funny exceptions and whatnot into your code. Rails seems to offer no
structure in its templates, and so they lack expressiveness.
Anyway, that's my impression; still a bit shallow, but more informed.
There's good things that I plan to borrow from Rails. But there's a
lot of things that feel unnecessary, or feel like paths I've explored
(or seen other people explore) and chosen not to follow. My
impression of Rails' success has been that, compared to Java and PHP,
it is really great. In no small part because those are really
crappy languages; low-level and lacking expressiveness. There are
also some important things Rails gets right, and I'm not going to
ignore those. There's some important things PHP gets right too, and
I try not to ignore those either. Java....... well, I guess it must
get some things right too. People besides myself will
have to figure out what to take from them.
In the end, there are some real distinctions between Rails and My
Ideal Framework that go beyond these nitpicky details I bring up
here; distinctions that are real tradeoffs, and where there can be
real differentiation between us and them. Not trivial things, like
blocks or threads or whitespace, but different perspectives on
programming. But I'll talk about that another time.