plugins in ringopress, ZipIterator in ringojs

[0 comments ] posted on 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.

altctrl on firefox 4

[0 comments ] posted on 25 Mar 2011

Hey, this post is on altctrl, my firefox extension. Today I have upgraded to firefox 4, and my extension was not compatible. I was about to start making the necessary changes in the .rdf file, but then i stopped and went to mozilla addon website and check if my extension is still used by anyone. To my surprise ~ 9 people every day use it. Thank you. And there was another goody from mozilla. A setting for the extension where you could change the maxVersion for which altctrl is compatible with. 1 click, and I was done, no code, no upload, no approval process. Thanks Mozilla.
If any user of altctrl reads this post, please get in touch with me here in the comments or by email and tell  me how you use it, and how usefull it is [or not].

One more thing, if you do not find the status-bar any more, and you want it back, go to View -> Toolbars -> Add-on Bar, or toggle Ctrl-/ . If you don't want the status bar, right click on the text of the extension [saveonshop] and then uncheck "Search prices onclick".


cheers.

google app on your own domain

[0 comments ] posted on 30 Dec 2010

If you love google and want to get more of their services, here is a post about hosting your web app [blog,personal page,company's page, full blown website] on their appengine cloud service, keeping email for your domain users with gmail and get the docs, calendar, picasa web albums and the other services included.

ok, time to start.
you buy the domain name me.com.
you can buy from your preferred registrar, or you can buy it from google too [godaddy.com powered] here: https://www.google.com/a/cpanel/domain/new. next steps:

  • go to http://www.google.com/a
  • choose the menu Solutions -> Google Apps (Free) or the other plans
  • on the next page, there is a button on the right: "Get Started". Click.
  • Enter your domain name: me.com
  • a big form comes up, with name, email phone etc... fill it up.
  • next we get a new form with username and password. fill it up.
  • now we login into our domain at google
  • an agreement. you agree.
  • a verification screen. you must choose between 2 ways of telling google that you indeed own the domain. i go for CNAME record
  • make a new CNAME record to point to google.com. done.
  • and we are in big part done with letting google provide services under our domain. for specific services , of course there are additional steps to follow.

email. i want to login into http://www.google.com/a/me.com [you can change that to http://mail.me.com] and have my users check user@me.com email. you get google spam filters and 7.5 GB of space and gmail interface for free.

In the dashboard panel, we have at the bottom some links to the services provided. click "Email".

  • on the page presented the important thing is "Email Activation" click: "Instructions on how to activate Email"
  • next, choose "Change MX Records".
  • and you change them in the DNS Manager - i am not providing advice on this one as it depends on your registrar, or if you host it yourself, depends on your name server daemon configuration files.
  • after you are done, click: "I have completed these steps".
  • test your setup now. send email to your me.com address, and reply to it. if it doesn't work, you should wait a few more hours in order for the DNS changes to propagate.

now, you want to have a website for your domain, and google appengine cloud solution seems like a good deal? read on.

  • go to https://appengine.google.com and after login, you will see a button: "Create application". Hit it.
  • name the application, and hit "Create application".
  • good. now go to "dashboard" on the page presented.
  • on the left we have a menu, go to "Administration -> Application Settings".
  • there is a section there:  Domain Setup, click the button: "Add domain...".
  • on the next page enter the domain: me.com
  • you will be presented with a login page of the me.com administration page.
  • accept the agreement and click "Activate this service"
  • "Add new URL" for the application. enter www.me.com
  • create a new CNAME in your DNS Manager for www to point to ghs.google.com.
  • hit "I've completed these steps"
  • now make your website, and upload it in the appengine cloud.

you are done.

of course there are more things to check out : sites service looks interesting too which is now scriptable in javascript and you can play with docs in the website, but it is enough for my needs right now.

testing appenginejs in ringojs console

[0 comments ] posted on 23 Dec 2010

For my ringopress project, i am doing a plugin system, and to test it, i am taking some wordpress plugins - the most popular ones - and implement them in javascript.

They say the most popular is akismet, so it seems that I have a job for tonight. I found a java akismet library - thank you ringojs and rhino for being on JVM - so it seemed like in 30 minutes i could go to bed.

I tested it a little in the ringo console, all fine, started google appengine local server and it said socket class use forbidden.

I forgot about that. Akismet java lib was using the apache httpclient and it boiled down finally to socket. Didn't mind that, i have been warned by google about this, happened as it should. good.

Well, let's make the akismet plugin with appenginejs lib based on urlfetch. I should use this lib in more places, though i still cannot drop the model and storage ringojs has built in, the abstraction is too nice.

First, let's play a little with it and do some exploratory programming. Before starting ringo console , we should place appenginejs package into ${RINGOJS_FOLDER}/packages/. next, place: ${JAVA_APPENGINE_GOOGLE_SDK_FOLDER}/lib/impl/appengine-api.jar,${JAVA_APPENGINE_GOOGLE_SDK_FOLDER}/lib/impl/appengine-api-labs.jar,${JAVA_APPENGINE_GOOGLE_SDK_FOLDER}/lib/impl/appengine-api-stubs.jar,${JAVA_APPENGINE_GOOGLE_SDK_FOLDER}/lib/impl/appengine-api.jar,${JAVA_APPENGINE_GOOGLE_SDK_FOLDER}/lib/testing/appengine-testing.jar into ${RINGOJS_FOLDER}/lib/ folder.

now we are ready to rock:

ringo >>

First, we have to setup a google appengine environment in order for appengine API calls work, and we do it as they do when they prepare unit testing environment.

>> var localServiceTestHelper = com.google.appengine.tools.development.testing.LocalServiceTestHelper; >> var localURLFetchServiceTestConfig = com.google.appengine.tools.development.testing.LocalURLFetchServiceTestConfig; >> var helper = new localServiceTestHelper(new localURLFetchServiceTestConfig());

If we would have wanted to play with storage, we would have used LocalDatastoreServiceTestConfig in addition to LocalURLFetchServiceTestConfig. you can see them all if you look inside appengine-testing.jar. next we do:

>> helper.setUp();

and we have our environment ready to serve us. You can place all above statements into a js file and execute it automatically at startup:

ringo -b gae-env.js

so you can be more productive.

i grabbed the example from here: http://www.appenginejs.org/docs#urlfetch

var fetch = require("google/appengine/api/urlfetch").fetch; var response = fetch("http://www.appenginejs.org"), html = response.content.decodeToString("UTF-8");

and there you go. GAE on your ringojs command line.

mootools on ringojs

[0 comments ] posted on 28 Nov 2010

My blog platform ringopress is getting moopowered from now on.
There are many reasons for this: i come from java world, and class, extends and such goodies is a good way for me to express myself in coding, then, my platform is getting more complex, and i try to reuse code, add more features [plugins ... ] and i cannot do it in javascript - i know it can  be done, but it beats me; also, i have experience in mootools from here and here . the ride in adding mootools into ringojs was not so easy as i presumed.
 First, mootools adds goodies to globals. The one i like and use is function's bind. ringojs has it too, as it has many of the things mootools adds to String, Array... . This won't be a problem unless you want to offer others the pleasure of having their own version of Array.each, or if you do not want to stumble into problems of for ... in ... Array. Unfortunately I did run across such constructs, and proposed a patch to Hannes, the main developer of ringojs. He accepted it, and right now it is ok. But i might not be so lucky with other people that will extend this blog platform [well, wishful thinking ... , but anyway... ]; they could do a for... in...Array and not know why they get more items. or the for each (var in Array).

There are a few ways out of this:

  1. you could hack the moootools lib and remove what they add to the global prototypes.
  2. you could hack the mootools lib and do a defineProperty with enumerable property set to false.
  3. you could leave it the way it is and advertise to the users of the platform it uses Mootools, and they are aware of it - that is what i chose to do for now.

 

 Secondly, ringojs maps requests to actions. I am using a feature of this mapping shown here: http://ringojs.org/wiki/Tutorial#urlmatching_argumentscapture where anything after /post/ is passed as a second argument to the function that handles the request.
unfortunately, the function in my case is presenting no arguments at all, as it is an inherited method mootools way.
here is in detail the problem:
at https://github.com/ringo/ringojs/blob/master/modules/ringo/webapp.js#L167 action is the function the handles the request. a check is done for the number of the parameters expected by the function. if a url comes like this: http://myserver.com/post/id and hits a function with only 1 parameter:
function(req){}
then ringojs gives you a 404 not found.
now, my Action is PostAction mootools class, and its process method is called when handlind the /post/id request.
but as you see here:
https://github.com/jgabios/ringopress/blob/mootoolish/WEB-INF/app/actions/PostAction.js#L9 it extends RingoPressAction, which in turn extends Action , and that's where i have process(req,url) method.
it does present 2 arguments in Action.js, but PostAction.process doesn't have them, its length returns 0.
it's a mootool thing.
so, i hacked into webapp.js and removed that IF where the check for action.length is done.
Other than that, everything is fine.

In ringojs examples i had the functions handling requsts presented in action.js module and exported. with mootools i managed to have them in separate files and inside classes.
huge benefits: instead of :
return Response.skin('skinname',context)
now i only specify the skinname in initialize method of the mootools class and that's it.
listing posts in the IndexAction and IndexAdminAction reuses lots of code, like getting the posts from db, managing pagination, etc...

More things can be taken away from Actions, like making a biz layer [yeah, the java way of solving problems: add 1 more layer of abstraction] that manages the ValueObjects, and a DAO layer, but that in the future.

for the moment i do not like the way i export the actions:
actually it is an instantioation of a mootools class, then a call its process method, then bind it to the object.
and I do it for every Action. sure it can be done somehow automatically in a general way.

Ringopress now has the master branch on github without mootools, and a branch here: https://github.com/jgabios/ringopress/blob/mootoolish/ with mootools in it. The mootoolish branch will get developed now, and if all gets well, it will make it into the master.