Ext.Direct remoting in Django

Well, as I didn't have a lot of other things weighing on me today, I created a Django implementation of the extdirect package I released earlier today. It's slightly more involved to set up, probably due to my not knowing Django nearly as well as Zope (suggestions welcome, Django enthusiasts), but after the initial setup writing the router classes is just as easy. Here's how to do it.

Read More...
Stumble Upon Toolbar

Ext.Direct remoting in Zope

With ExtJS 3.0 came Ext.Direct, an excellent library for remoting server-side methods to the client side. We had already made the decision to switch to Ext with the revamped Zenoss UI, so I'd been working with Ext quite a lot; in the upcoming 2.5 release, there's a brand-new event console that makes heavy use of Ext.Direct. After learning its ins and outs with all that work, I decided to write a Python version of the server-side component to make it easier to use; I then went further and created a Zope 2 and 3 compatible component that makes it trivial. extdirect is the result. Here's some brief instruction on how to use it.

Read More...
Stumble Upon Toolbar

Vim, Part II: Matching Pairs

One of the more standard vim tweaks is to have parens, braces, brackets, and quotes auto-close—that is, you type (, and the ) is inserted automatically. This can be a big help in keeping track of nested groups. This is easy to do poorly; to do it well takes a little more thought. I took a look at TextMate's rules and mimicked them (mostly). Put all this stuff in your .vimrc (_vimrc on Windows).

Note: A couple of these tweaks are modified or copied outright from the Vim Scripts repository.

Auto-close pairs


The initial auto-close is quite easy; leaving the group is slightly more involved. If you're inside parens, at the end, and you want to leave them, instinct is just to type the closing ); without a little logic, however, that will in no way achieve the desired result. The rule is: if typing the closing character would end the block without auto-close (that is, because it's the character to the immediate right of the cursor), it should end it with auto-close. Here's how you achieve that.
  1. Map the auto-close for non-quotes. This is trivial:
    inoremap ( ()<Left>
    inoremap [ []<Left>
    inoremap { {}<Left>
    autocmd Syntax html,vim inoremap < <lt>><Left>

    No big deal; when the opening character is typed, insert the closing character and hit <Left> so the cursor is between them.

  2. Function to check next character.
    function! ClosePair(char)
    if getline('.')[col('.') - 1] == a:char
    return "\<Right>"
    else
    return a:char
    endif
    endf

  3. Map closing characters. Now, all you need to do is link the closing characters to the function:
    inoremap ) <c-r>=ClosePair(')')<CR>
    inoremap ] <c-r>=ClosePair(']')<CR>
    inoremap } <c-r>=ClosePair('}')<CR>

  4. Map the auto-close for quotes. This is only slightly more difficult than ()[]{}, because of the case where you might add an escaped quotation mark to a string. It requires a separate function: since the opening and closing characters are the same, we can't map the closing to ClosePair. So we put it all in one function:
    function! QuoteDelim(char)
    let line = getline('.')
    let col = col('.')
    if line[col - 2] == "\\"
    "Inserting a quoted quotation mark into the string
    return a:char
    elseif line[col - 1] == a:char
    "Escaping out of the string
    return "\<Right>"
    else
    "Starting a string
    return a:char.a:char."\<Left>"
    endif
    endf

    Then just need to map that:
    inoremap " <c-r>=QuoteDelim('"')<CR>
    inoremap ' <c-r>=QuoteDelim("'")<CR>


Dump all that in to .vimrc, source it or restart vim, and try typing some stuff! You may notice a few places where it isn't quite what you'd want, though, which brings us to our next two tweaks.

Wrap visually selected text


One of the nicer minor features of TextMate is its treatment of highlighted text. If you have something highlighted and type a, it replaces the text, like other editors. If you type (, however, it wraps the selected text in parentheses. This is enormously useful. Luckily, it's very easy to recreate in Vim:
vnoremap (  <ESC>`>a)<ESC>`<i(<ESC>
vnoremap ) <ESC>`>a)<ESC>`<i(<ESC>
vnoremap { <ESC>`>a}<ESC>`<i{<ESC>
vnoremap } <ESC>`>a}<ESC>`<i{<ESC>
vnoremap " <ESC>`>a"<ESC>`<i"<ESC>
vnoremap ' <ESC>`>a'<ESC>`<i'<ESC>
vnoremap ` <ESC>`>a`<ESC>`<i`<ESC>
vnoremap [ <ESC>`>a]<ESC>`<i[<ESC>
vnoremap ] <ESC>`>a]<ESC>`<i[<ESC>

Backspace in empty pair deletes both


Now that you have auto-closing parens, let's say you type one by mistake. Unfortunately, deleting it now involves two extra keystrokes (<ESC>xx or <Right><Bksp><Bksp> instead of <Bksp>). TextMate gets around this by deleting both if you're in an empty block. I was tired of mapping the same thing for every pair, though, so I wrote a function that uses the matchpairs option, which exists to tell Vim what delineates a discrete block, to figure it out dynamically (thanks to Aristotle Pagaltzis for cleaning it up!). It checks whether the characters on either side of the cursor constitute a valid pair:
function! InAnEmptyPair()
let cur = strpart(getline('.'),getpos('.')[2]-2,2)
for pair in (split(&matchpairs,',') + ['":"',"':'"])
if cur == join(split(pair,':'),'')
return 1
endif
endfor
return 0
endfunc

And then the actual deletion function:
func! DeleteEmptyPairs()
if InAnEmptyPair()
return "\<Left>\<Del>\<Del>"
else
return "\<BS>"
endif
endfunc

Then map it to Backspace, so it's called whenever Backspace is:
inoremap <expr> <BS> DeleteEmptyPairs()

So, in plain English: when Backspace is hit, we check to see if the characters on the immediate left and right of the cursor are part of a matching pair, looking at the matchpairs option to see what counts of a valid pair. If we are in a pair, delete both characters; otherwise, send a normal Backspace.

Next (and probably last) in the series: Whatever's left in my .vimrc, namely, assorted tweaks to make one's coding life easier.

Read More...
Stumble Upon Toolbar

Vim, Part I: Improved Python syntax, Blackboard color scheme

Every few months, I notice that TextMate exists and try to use it; inevitably, the requirement that I use arrow keys defeats the endeavor. It has a few features, however, that I like, most of which I've ported to vim, my primary text editor. These features are too numerous for a single post, so I'll highlight them in a series. First up: An improved Python syntax definition and my favorite color scheme.

Python Syntax


The most popular Python syntax highlighting script is pretty complete, but it's missing a few things (Update: several of the improvements I made a year ago have been added to Dmitry Vasiliev's script, such as decorators, doctests, imports, etc. I've merged the two, so this is slightly less special than before). Specifically: docstrings, self, arguments/base classes, assignment as distinct from other operators, and a few other little things. TextMate had these, so I ported them, and the results were pretty good.

Download the improved syntax file and drop it into ~/.vim/syntax.

Blackboard Color Scheme


TextMate had a color scheme that I loved: Blackboard. The colors were primaryish without being irritatingly bright or clashing; keywords, builtins, and class definitions were given appropriate prominence. I ported it to Vim. Here's what it looks like:



That example doesn't include a doctest, but they are highlighted separately in the same color as builtins.

Download the color scheme and drop it into ~/.vim/colors, then do :colorscheme blackboard to see the results. Pretty easy on the eyes, no?


Next in my Vim series: automatic handling of pair completion (and deletion).

Read More...
Stumble Upon Toolbar

Plaintext task list from OmniFocus with AppleScript

I live in OmniFocus most of the day. I have lots of perspectives set up to help me parse my various responsibilities (see my previous post, "How I Use OmniFocus," for more details); in particular, I have one perspective called "Global Tasks" (View Mode: Context, Filter: Active, Grouping: Ungrouped, Sorting: Due, Action Filter: Next Action) that essentially gives me a list, ordered by dueness, of what I should be doing right now.

It helps me focus to have that list of tasks constantly in front of my eyes. This is counterbalanced by my desire to save screen real estate for the assorted terminal windows and browsers I need up to do my job; OmniFocus takes up valuable real estate. So I set about writing a quick script to pull tasks from OmniFocus, format them, and return them as plain text, which I could embed on my desktop with GeekTool.

set taskList to ""
tell application "OmniFocus"
tell the default document to tell the front document window
set perspective name to "Global Tasks"
set oTrees to trees of content
set n to count of oTrees
repeat with i from 1 to n
set oTask to value of (item i of oTrees)
set taskTitle to name of oTask
set projTitle to name of containing project of oTask
set taskList to taskList & taskTitle &
" (" & projTitle & ")\n"
end repeat
end tell
end tell
return taskList

I saved that as ~/src/omnifocus_tasks.scpt and added a Command item to GeekTool that ran osascript /Users/ian/src/omnifocus_tasks.scpt every minute. The result was exactly what I wanted:



Now, even if I hide OmniFocus to make room for something else, I can't forget to check for my next tasks and keep everything up to date.

Read More...
Stumble Upon Toolbar

Rapid development egg to RPM with buildout

I'm developing a feature for Zenoss that lives in its own Zope product. For the time being, it's fairly independent of Zenoss and can be demoed in a separate Zope instance. So I have a buildout for the Zope environment, also setting up a copy of the new product as a development egg (see this tutorial for an example of how to set that up). I have that development egg pulled into the Zope demo buildout via svn:externals.

At certain points in the development cycle, however, I want to send QA an RPM of the demo instance, with my package installed, so they can test it. This is kind of a hassle if I have to build the egg of the product and get it in the buildout directory on the build box in order to generate an RPM. Nor can I simply leave it as a development egg, because the package wouldn't be in the same place on the test box, so the link would fail.

So here's an easy way to turn a development egg into a production egg for the purposes of creating an RPM, using a separate buildout config file.

You'll want to read this tutorial on basic RPM creation using zc.buildout. In addition, the buildout.cfg will include a develop option in the buildout section:

[buildout]
...
develop = src/Products.MyCustomProduct

(src exists in my demo directory, and has the svn:externals definition pointing at the custom package.) The egg is also included in the instance section:
[instance]
...
eggs =
${buildout:eggs}
Products.MyCustomProduct

zcml = Products.MyCustomProduct

This gets me a development buildout, in which I can work on the package in src/Products.MyCustomProduct without building eggs or anything.

Now when it comes time to build the RPM, we need to modify the rpm config file from the tutorial to build Products.MyCustomProduct as an egg, then install it. We'll use the z3c.recipe.egg, which lets you run setup.py in a source package with arbitrary arguments. Here's the new rpm.cfg:
[buildout]
extends = buildout.cfg
develop =
parts =
MyCustomProduct-build
zope2
instance
zopepy
myzope

[MyCustomProduct-build]
recipe = z3c.recipe.egg:setup
setup = src/Products.MyCustomProduct
args = bdist_egg

[instance]
find-links =
${buildout:find-links}
${MyCustomProduct-build:setup}/dist

[myzope]
...

Explanation. So, the egg is already referenced in buildout.cfg as one of the instance eggs. All we do in this one is remove its declaration as a development egg (with develop= in the buildout section), add a part to create the egg artifact (MyCustomProduct-build), and tell the instance section where to find it (expanded find-links).

z3c.recipe.egg just needs the setup option defined, which points at a package directory containing setup.py. It'll install by default, whereas we just want it to build an egg, so we specify custom args with the args option.

Notice that we have to build the egg first. This is obvious upon reflection, but the first time I just added MyCustomProduct-build to the parts instead of making sure it was first, which meant that buildout couldn't find it in the instance step, because it didn't exist yet.

This will create a deployment instance just fine on its own, in the current directory. But in the RPM example, we build out somewhere else, so we'll have to update the buildout command in the spec file so that it can tell buildout where to find the source of the custom package. This is a modification to the %build script:
%build
...
bin/buildout -c rpm.cfg buildout:directory=%{installdir} MyCustomProduct-build:setup=%{sourcedir}/src/Products/ZenUI3


Then we build the RPM as in the tutorial. This time, it'll package up the egg in its current state and install that artifact into the Zope instance. This means that I can generate an RPM for QA on the fly with a single command, which is lots easier than assembling the parts manually.

Read More...
Stumble Upon Toolbar

Using zc.buildout to generate an RPM

zc.buildout is a project that allows one to define the steps to create an application environment in config files. It is, roughly, a Makefile replacement, or at least can be used that way. It makes it very easy to reproduce deployments, both for development and for production, with nothing but a bootstrap and a config file.

The natural next step, of course, would be to use buildout to generate installable packages like RPMs. This is less straightforward than one might expect. There are probably many ways to do it, as buildout is very versatile; I'll explain one method I've come up with after the jump.

(My rpm knowledge is pretty minimal, so this may not be strictly according to doctrine, but it seems to work flawlessly in the scenarios I've tested.)

I'll use as an example the installation of a vanilla Zope 2 instance, with an rc script to control it. One may of course add one's own products to the buildout, thereby creating a custom application; that is beyond the scope of this example (see Martin Aspeli's excellent Plone-oriented but generally informative tutorial, along with the buildout docs, etc.).

Here's the config for a Zope 2.11.2 buildout:

[buildout]
parts =
zope2
instance
zopepy

find-links =
http://dist.plone.org
http://download.zope.org/distribution/
http://effbot.org/downloads

# Add additional eggs here
eggs =

# Reference any eggs you are developing here, one per line
# e.g.: develop = src/my.package
develop =

[zope2]
recipe = plone.recipe.zope2install
url = http://www.zope.org/Products/Zope/2.11.2/Zope-2.11.2-final.tgz

[instance]
recipe = plone.recipe.zope2instance
zope2-location = ${zope2:location}
user = admin:admin
http-address = 8080
debug-mode = on
#verbose-security = on

# If you want Zope to know about any additional eggs, list them here.
# This should include any development eggs you listed in develop-eggs above,
# e.g. eggs = ${buildout:eggs} ${plone:eggs} my.package
eggs =
${buildout:eggs}

# If you want to register ZCML slugs for any packages, list them here.
# e.g. zcml = my.package my.other.package
zcml =

products =
${buildout:directory}/products

[zopepy]
recipe = zc.recipe.egg
eggs = ${instance:eggs}
interpreter = zopepy
extra-paths = ${zope2:location}/lib/python
scripts = zopepy


Notice I'm using Plone's recipes for setting up a Zope instance; they work as well as you might hope. If you bootstrap and buildout from this config file, you'll get yourself a Zope server listening at 8080, and a custom interpreter with the right paths and everything.

Make a new directory containing the above config file, saved as buildout.cfg, and a copy of bootstrap.py. If you like, you can make sure it works with:

$ python bootstrap.py
$ bin/buildout
$ bin/instance fg

That'll start up Zope in the foreground.

A very nice feature of zc.buildout is the ability of config files to extend others. We'll use this to create a separate buildout with the extra/different features we need for an RPM. In this minimal example, the differences are few, but you can see how it would work well for more complex buildouts. Save this as rpm.cfg in the same directory as buildout.cfg:

[buildout]
extends = buildout.cfg
parts += myzope

[myzope]
recipe = zc.recipe.rhrc
parts = zoperc

[zoperc]
run-script = ${buildout:directory}/bin/instance


Building out with this would do the same thing as our buildout.cfg, but would also install /etc/init.d/myzope as a control script, thanks to zc.recipe.rhrc.

Now we get to the (slightly) tricky part. Normally, one would build an RPM by creating a source tarball, dropping it in /usr/src/redhat/SOURCES, and pointing a spec file at it. We could do that here if we wanted to. We could also use zc.sourcerelease to create a tarball, including egg dependencies and whatnot, and do the same thing (though that irritatingly requires one to build out twice). But it's an extra step.

The directory option to buildout lets you build out into a directory other than the current. You can also override the config file from the command line. So the simplest method to create an RPM is just to pass the install path to buildout in the spec file. Here's a spec file that works (save it as myzope.spec in the same directory as rpm.cfg):

%define name myzope
%define sourcedir %(echo $PWD)
%define installdir /opt/%{name}
%define rcscript /etc/init.d/%{name}

Name: %{name}
Version: 1.0
Release: 0
Summary: My Zope Instance
URL: http://www.zope.org
License: GPL
Vendor: Me
Packager: Me <me@example.com>
Group: Applications/Database
Buildroot: /tmp/%{name}-buildroot

%description
%{summary}

%prep
rm -rf %{rcscript} $RPM_BUILD_ROOT %{installdir}
mkdir -p $RPM_BUILD_ROOT %{installdir}

%build
cd %{sourcedir}
python bootstrap.py
bin/buildout -c rpm.cfg buildout:directory=%{installdir}

%install
echo "effective-user nobody" >> %{installdir}/parts/instance/etc/zope.conf
mkdir %{installdir}/products
mkdir -p $RPM_BUILD_ROOT/etc/init.d
mkdir -p $RPM_BUILD_ROOT%{installdir}
mv %{rcscript} $RPM_BUILD_ROOT%{rcscript}
mv %{installdir}/* $RPM_BUILD_ROOT%{installdir}

%files
%defattr(-, root, root, 0755)
%{rcscript}
%defattr(-, nobody, nobody, 0755)
%{installdir}

%clean
rm -rf %{rcscript} $RPM_BUILD_ROOT %{installdir}


Now, step by step. First of all, the clever bit:

%define sourcedir %(echo $PWD)

This lets us find the directory with the buildout config—provided, of course, that the spec file lives in the same directory as rpm.cfg, and that you run rpmbuild from there. Notice that we also define the directory in which this will be installed, namely /opt/myzope.

The rest is basic spec file stuff, until you get to the build script:
%build
cd %{sourcedir}
python bootstrap.py
bin/buildout -c rpm.cfg buildout:directory=%{installdir}

So we're using the rpm config file, but overriding the directory option in the buildout section to use /opt/myzope as the location in which to build out the environment. We have to do it this way—instead of, say, building out in $RPM_BUILD_ROOT—because the paths in the buildout would then refer to /tmp/myzope-buildroot/opt/myzope instead of /opt/myzope.

Then it's as simple as doing a couple Zope-specific steps (setting the user who runs Zope), copying the resulting environment into the build root (not forgetting /etc/init.d/myzope) and letting it be packaged up.

Build it with rpmbuild -bb myzope.spec. Shizam, an RPM that you can install. Start it up with /sbin/service myzope start and check it out in the browser.

There are a few other tricks involved in separating a more complex development buildout from one that is ready for an RPM. I'll explain further in my next post.

Read More...
Stumble Upon Toolbar