Cheri may be installed as a gem (gem
install cheri), or downloaded as a zip file from the Cheri project
page. The Cheri::Swing and CJX components require JRuby version 1.0.3
or later; the other components run on C (MRI) Ruby as well.
Background
Cheri grew out of the JRBuilder
project, a Groovy-SwingBuilder-style
builder for JRuby. JRBuilder worked well enough, but was
difficult to extend, or integrate with other builder
components. Cheri is designed to allow multiple builder
components to be used together. It also provides a means to
extend builders without subclassing them, so multiple
extensions may be used concurrently. (The Cheri::Swing and Cheri::Html
example on the right demonstrates two builders being used
seamlessly.)
Cheri::Swing is the direct descendant of JRBuilder, though
it retains little of the original code. (There is also a
Cheri::AWT component, but it exists mostly to support
Cheri::Swing.) Cheri::Html originated as a sort of
proof-of-concept trial of the generality of Cheri's
architecture. In fact, it took only about 30 minutes to write a
simple Cheri::Html, but the exercise lead to a number of
improvements to the Cheri model (and lead me to embark on a
more complete Cheri::Html). Cheri::Xml came about because,
well, the code was pretty much there in Cheri::Html, so it
would have been silly not to include it.
Though it's fairly easy to create new builders using the
Cheri framework, I felt an even simpler mechanism would be
useful. The Cheri builder-builder provides a means to create
relatively simple builders without delving into the workings of
the Cheri framework. It also provides a means to extend
existing builders. (The extending
Cheri::Swing example on the right demonstrates one way
of extending Cheri::Swing with additional classes.)
Common features
Cheri builders are mixin modules; to use one, you include it
in a class. The builder's functionality is available to
instances of that class, and any subclasses (unless the
including class is Object — inclusion in Object / at the
top level is supported, but discouraged; inheritance is
disabled in that case).
Cheri builders implement method_missing in the classes in which
they're included; in normal operation, that is how Cheri
receives the symbols that are mapped to class (or method) names
for objects to be constructed. (As you will see, however, it is
possible to use Cheri builders without relying on method_missing.) If no included builder can
resolve a symbol received by method_missing, Cheri calls super to pass the call along, in case other
code inherited by the class relies on it; likewise, if your
class must implement method_missing,
it can still work smoothly with Cheri builders as long as it
passes along methods it doesn't consume:
class MyClass
include Cheri::Xml
#...
def method_missing(sym,*args)
if my_sym?(sym)
#...
else
super
end
end
end
All Cheri builders also implement a cheri method, which plays two roles,
depending on how it's called. When called with a block, the
cheri method enables Cheri builder
syntax within its scope for all included builders. When called
without a block, it returns a CheriProxy object that can act as
a receiver for builder methods, for any included builder. (For
this reason, it's often referred to in Cheri code as a proxy
method, though that isn't completely accurate.)
include Cheri::Swing
#...
@f = frame 'My Frame' #=> NoMethodError
#=> (no builder in scope)
@f = cheri.frame 'My Frame' #=> JFrame instance
cheri {
@f = frame 'My Frame' #=> JFrame instance
}
Each built-in Cheri builder also supplies its own proxy
method (in addition to the cheri
method): swing for Cheri::Swing (and
also awt, since Cheri::Swing includes
the little-renowned Cheri::AWT), html
for Cheri::Html, and xml for
Cheri::Xml. These methods play the same dual scoping/proxy
roles as the cheri method, but apply
only to their respective builders. (Each also provides
additional functionality; see the sections on individual
builders for details.) The builder-specific proxy methods also
serve to disambiguate overloaded builder method names.
Consider:
include Cheri::AWT #=> uncommon, but legal
include Cheri::Swing
include Cheri::Html
# HTML frame? Swing frame? AWT frame?
@f = cheri.frame
# (answer: whichever was loaded last)
@af = awt.frame #=> java.awt.Frame
@sf = swing.frame #=> javax.swing.JFrame
@hf = html.frame #=> HTML frame
#=> (Cheri::Html::EmptyElem)
The proxy methods can also be used in any case where a
conflict exists with a defined method with the same name (since
method_missing only works if the
method is, um, missing). A common conflict arises with the HTML
p element; I normally undef p when using Cheri::Html, but could just
use html.p instead. In fact, as noted
earlier, it's possible to avoid method_missing altogether, if you have
reason to do so (and are willing to forgo the aesthetic
pleasure of unencumbered builder syntax):
$stdout html{
html.body{
html.table{
html.tr{html.td 'Hi Mom'}
}}}
Stay tuned…
This is a beta release of Cheri — more features are in
the works, and more documentation is on the way, so be sure to
check back from time-to-time. Feel free to post bugs or
questions on the forums or mailing
list at the Cheri
project/download page.
Bill Dortch
22 June 2007
