plugins in ringopress, ZipIterator in ringojs - 09 Apr 2011



Continuing my work on ringopress , I needed an import feature for the plugins. Being on GAE, I do not have the possibility as other blog platforms have, to upload them by ftp in a certain folder. So I thought it would be great to have a central repository or private repositories where people can host them, and in the admin interface you could point to a url and import the plugin, through web, into the google datastore.

The plugin would be a zip file, containing 2 files, a metainf file with a json object inside, providing name, version, email, author information, and a file with the actual code.

My model for plugin is:

var Plugin = db.Model("plugin", { code: new db.TextProperty(), lastModified: new db.DateProperty(), name: new db.StringProperty(), version: new db.StringProperty(), author: new db.StringProperty(), email: new db.StringProperty(), hook: new db.StringProperty(), type: new db.StringProperty(), activated: new db.BooleanProperty() });

The plugin gets executed by calling require('pluginname.gae'), which is a modified "require" method that goes straight into google datastore, gets the pluginname entity, gets its "code" property, transforms it into a stream and pipes it into the usual ringojs java system [GAEResource class is responsible].

Next, I want to share how one can get a zip file from the web, unzip it, and do something with its contents.

There are 2 classes that deal with Zip resources in ringojs:

ZipFile and ZipIterator. ZipFile is out of the question as my plugins are on the web, so I turned to ZipIterator, which receives a stream or a string as parameter.

All I had to do is transform my web resource into a stream, and then ZipIterator would take over and give me zip entries. Being on GAE, urlfetch is the only way to do web requests: var url = env.req.params['pluginzipurl']; // comes from the form var pluginZipResource = require("google/appengine/api/urlfetch").fetch(url);
Unfortunately, I tried MemoryStream on pluginZipResource.content [which is a Binary] , but ZipIterator does not work with MemoryStream as it is not a "Stream" per se or wrapper of java InputStream, but a js Object and no way to get the inputStream property. so I am left to use java: var stream = new java.io.ByteArrayInputStream(pluginZipResource.content.toByteArray()); var entryGenerator = require('ringo/zip').ZipIterator(stream);

To my surprise, ZipIterator is a new beast in js land [well, at least to me]. It is a generator object, kind of a java iterator on which you can call next() to get to the next item in the collection. A particular thing about it is that it gets executed [it is a function, don't call "new ZipIterator" in your code] only when you call next() on the returned object. Also, inside, it has "yield entry", and it is not at the end of the function, but quite in a middle of a while statement. This is the "return" for the generator when "next()" is called. When no more items are available, next() throws an exception. This is how you can get the zip entries: try{ var entry = entryGenerator.next(); var pluginStream = new io.TextStream(entry); var line = null; while(line !== ''){ line = pluginStream.readLine(); pluginJSONObj+=line; } } catch(e) { // handle exception }

For those of you who want to see the whole code and how it integrates into something else, see my addplugin.js file.

No comments yet.
Leave a comment:

author
email
website
Comment