After trying almost every web framework for Ruby out there, I still could not find one I really liked (Wee was close, but has non-restful URLs), so I simply decided to grow my own (hey, you know me ;-)), called Rye.
Rye is a framework of the different kind. I don’t have a lot of code yet, and lacks a lot of functionality, but I could extract the basic idea already and will try to explain it here.
Rye provides an RESTful interface to stored objects. So far, my objects are simply stored as YAML files, but that will not scale for bigger amounts of data. Rye maps these objects onto URLs like this:
http://<server>/<object-path...>/<method>/<arguments...>?<query>
So far, Rye can handle the two most commonly used (and supported,
sigh) HTTP methods, GET and POST. In the case of GET, Rye will
retrieve/load the object, call the method on it (possibly with the
arguments and the parsed query) and, depending on the return value
of the method, render the generated page or call show
, the default
renderer for the object. POST is treated similary, except that the
object is saved to the store again before rendering the site.
Therefore, GET can’t easily change any values and is idempotent, as
required by REST. (Of course, you still can—and are free to—shoot
yourself in the foot by manipulating the storage on your own, but it
will really be your fault.)
The nice benefit of this is that there is not much to do to use Rye:
Usually (so far, at least) you simply define a few YAML specific
methods to store only the required instance variables of the object
and make your class inherit from Rye::Base
, which defines a few
helper methods and attributes (maybe this should get a mix-in).
As an example, I developed a very simple to-do list manager:
class ToDoList < Rye::Base
attr_accessor :title
attr_accessor :todo
attr_accessor :done
def initialize(title)
super()
@title = title
@todo, @done = [], []
end
def to_yaml_properties; ["@title", "@todo", "@done"]; end
def show; template; end
def add(params); @todo << params["item"]; self; end
def finish(item)
@done << item if @todo.delete item
self
end
def undo(item)
@todo << item if @done.delete item
self
end
end
As you can see, this is straight-forward Ruby code without any Web
specific stuff—except maybe the show
method that calls template
,
a helper defined in Rye::Base
that renders the object using a
class-specific Kashmir template (of course, you can easily interface
with your favourite templating engine).
If you wonder why finish
and undo
return self
, well, that means
that Rye will redirect to show
them, a useful and logic convention.
finish
and undo
do change data, and therefore need to be called
using POST. As of now, you would call finish("blubb")
by writing HTML
like this:
<form action="/myapp.rye/finish/blubb" method="post">
<input type="submit" value="Finish blubb!" />
</form>
If you decide to pass additional values (for example, to call add
using a form) you can do that too, as usual:
<form action="/myapp.rye/add" method="post">
<input type="text" name="item" />
<input type="submit" value="Add!" />
</form>
Since the code essentially only contains the real application logic, it is very easy to unit test. In fact, you test it just like any other class. (Nevertheless, I may add a helper library for unit testing which I dub “ergot” :-))
So far, Rye is only about 150 lines of code, but already covers a lot of the basic functionality. I’ll need to add a generic store, so one can use databases (for example with Og) too and isn’t forced to use YAML. Maybe I get a first release done this week (or possibly not, who knows).
I’d be interested in hearing your comments on Rye, just comment here or mail me.
NP: 33hz—Digital Lover