Tuesday, April 14, 2009
Another Training
Monday, April 13, 2009
Dynamic Columns In Ext JS Grid
What I needed was a column model dynamically adjusting depending on the data received from data store (i.e. JSON provided by server). Something like the following:
Where each site has a different set of dimensions (rows) and different set of auditors (columns).
It turned out that the problem was actually two-fold - first dynamically change the columns and then display the editable checkboxes instead of true/false values.
To change the columns I used metaData attribute in my JSON. Including metaData attribute causes Ext.data.JsonReader to be adjusted to the new set of fields and other config parameters and fire metachange event. Here's an example of the JSON with metaData:
{ "results": 3, "auditors": [ {"dimension_name": "Audits and Reviews", "auditor_3": false, "dimension_id" : 10, "id": 10, "auditor_4": false, "auditor_18": false}, {"dimension_name": "Emergency Management & Fire Safety", "auditor_3": false, "dimension_id": 8, "id": 8, "auditor_4": false, "auditor_18": false} . . . ], "metaData": { "fields": [ {"name": "id"}, {"type": "string", "mapping": "dimension_name", "name": "auditor[dimension]"}, {"type": "int", "mapping": "dimension_id", "name": "auditor[dimension_id]"}, {"type": "bool", "name": "auditor_3", "header": "Dasha Alexo"}, {"type": "bool", "name": "auditor_18", "header": "Tester 3"}, {"type": "bool", "name": "auditor_4", "header": "Zobak Zobakovic"}], "root": "auditors", "totalProperty": "results", "id": "id" } }In this case it's the number of auditors (and their names) that's changing. My data types are booleans but displaying any other data type would look pretty much the same.
Next I needed to update the column model based on the new meta data. I did that by adding metachange listener to my store
auditors_store.addListener("metachange", function(store, meta){ var grid = Ext.getCmp('auditors-grid'); var columns = [ {header: 'Dimension', dataIndex: 'auditor[dimension_id]', hidden: true}, {header: 'Dimension', dataIndex: 'auditor[dimension]', width: 200} ]; for (var i = 3; i < meta.fields.length; i++ ) { var plugin = new Ext.grid.CheckColumn({ header: meta.fields[i].header, dataIndex: meta.fields[i].name, grid: grid, width: 120 }); columns.push(plugin); plugin.init(grid); //use columns.push( { header: meta.fields[i].header, dataIndex: meta.fields[i].name, type: meta.fields[i].type }); for fields that don't require plugins } grid.reconfigure(auditors_store, new Ext.grid.ColumnModel(columns)); });I had to create and initialize the plugin for each new column as I needed editable checkboxes. For normal fields just use the usual column config object { header: meta.fields[i].header, dataIndex: meta.fields[i].name, type: meta.fields[i].type }.
The actual magic happens is in the last line. Grid has an obscure method reconfigure( Ext.data.Store store, Ext.grid.ColumnModel colModel ) that allows to switch the store and column model (the columns displayed) on the fly.
My next problem was to make the checkboxes editable. To do that I had to put a small fix to the CheckColumn plugin (see the plugin) as the original plugin assumes that the grid is not rendered when initializing, however, when dynamically adding columns the grid is already rendered so I had to change the following:
init : function(grid){ this.grid = grid; this.grid.on('render', function(){ var view = this.grid.getView(); view.mainBody.on('mousedown', this.onMouseDown, this); }, this); },to the following:
init : function(grid){ this.grid = grid; if (this.grid.rendered) { var view = this.grid.getView(); view.mainBody.on('mousedown', this.onMouseDown, this); } else { this.grid.on('render', function(){ var view = this.grid.getView(); view.mainBody.on('mousedown', this.onMouseDown, this); }, this); } },And that's all - nice and simple. If only there were more books on Ext :-(
Wednesday, April 8, 2009
Agile Employment Contracts (part 1)
Tuesday, April 7, 2009
Ubuntu 8.04 mod_rails
sudo apt-get install apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 ssl-cert apache2-prefork-dev sudo gem install passenger sudo passenger-install-apache2-moduleNow add the 3 generated lines (almost last on the screen) to config file:
sudo vim /etc/apache2/apache2.confRight now it looks something like the following - BUT THE VERSION NUMBERS WILL CHANGE SO COPY THE LINES FROM THE PREVIOUS PROCESS
LoadModule passenger_module /usr/local/lib/ruby/gems/1.8/gems/passenger-2.1.2/ext/apache2/mod_passenger.so PassengerRoot /usr/local/lib/ruby/gems/1.8/gems/passenger-2.1.2 PassengerRuby /usr/local/bin/rubyIn the same config file add the server name and NameVirtualHost otherwise the server will complain at restart:
ServerName XYZ NameVirtualHost *:80save the config file and continue - we'll need to enable modrewrite and disable default site:
sudo a2enmod rewrite sudo a2dissite defaultnow just configure your application by adding your_app_name to /etc/apache2/sites-available/ with the following content:
sudo vim /etc/apache2/sites-available/your_app_name
<VirtualHost *:80> ServerName abc.def.com ServerAlias xyz.com DocumentRoot /home/bohm/rails/access/current/public </VirtualHost>where abc.def.com and xyz.com are the URLs of your application and DocumentRoot points to the public folder of your application. To enable the application run
sudo a2ensite your_app_name sudo /etc/init.d/apache2 reloadTo disable it run
sudo a2dissite your_app_name sudo /etc/init.d/apache2 reloadAnd you're done :-)
Monday, April 6, 2009
Multiline Tree nodes in ExtJS
new Ext.tree.TreePanel({ //renderTo:'treecontainer', id: 'upcoming_events', loader: new Ext.tree.TreeLoader({ dataUrl:'/datasource/upcoming_events_json', baseParams: {}, baseAttrs: {uiProvider: Ext.ux.tree.MultilineTreeNodeUI} }), root: new Ext.tree.AsyncTreeNode({ expanded: true }), rootVisible: false, collapsed: false })Just include the MultilineTreeNodeUI.js in the page and create a usual TreePanel. The only difference is to include baseAttrs: {uiProvider: Ext.ux.tree.MultilineTreeNodeUI} in your loader. Multilne tree requires the data in the following format:
{ text: '09 April 2009', iconCls: 'calendar', details: ['Program for the day...'], uiProvider: Ext.ux.tree.MultilineTreeNodeUI, children:[ { text: '9:30', iconCls: 'time', details: ['Win $1M', 'Sure win...'], uiProvider: Ext.ux.tree.MultilineTreeNodeUI, leaf: true }, { text: '11:30', iconCls: 'time', details: ['Buy private jet', 'Or something'], uiProvider: Ext.ux.tree.MultilineTreeNodeUI, leaf: true } ] }Where the additional lines are provided by details attribute. It's an array - one item per line - maybe not an ideal solution but still workable - worst case you just split the longer line by number of characters. The uiProvider will generate the multiple lines. Your controller doesn't have to do anything about it as it will be added automatically by the baseAttrs: {uiProvider: Ext.ux.tree.MultilineTreeNodeUI} we added in the loader.
Saturday, April 4, 2009
SMS with GSM Modem
wget http://smstools3.kekekasvi.com/packages/smstools3-3.1.3.tar.gz tar -xzf smstools3-3.1.3.tar.gz cd smstools3 sudo ./install.shI am using Huawei Mobile Modem E270 and E170 from Starhub. The only change I had to make now is update the config file. Here's my config file /etc/smstools.conf
devices = GSM1 logfile = /var/log/smsd.log loglevel = 7 [GSM1] device = /dev/tty.HUAWEIMobile-Modem incoming = yes #pin = 1111 init = AT+CNMI=2,0,0,2,1After that just start/restart the smsd daemon
sudo killall smsd; sudo smsd;
sudo sendsms +6584199983 'Your SMS message....'
To: 491721234567 Hello, this is the sms.More info about the structure of the file is here.
SMS Integration Without Hardware
When I tried to integrate one of our applications with SMS messages (both incoming and outgoing) several years ago we didn't manage to find not even one SMS gateway provider that would be able to pass our simple criteria. Allow sending (da :-) and receiving of messages with some reasonable notification of received messages (simple webservice / forward to email, anything). The replies should preferably be handled by a local number and sending/forwarding should be within reasonable price range :-) Is it too much to ask?
There is a number of online portals for sending (some support also receiving) SMSes like Clickatel, SMSZilla, SMSCountry. Their pricing per message varies from SGD0.05 - 0.1 which is quite reasonable and some provide nice and easy API for sending (when you think about it how complicated can it get to write a form handler with 3 or 4 fields) - some ask you to pay for API extra. For better or worse they would accomplish the sending but the receiving was either very painful or expensive or both. On top of that sending a hundred messages to and Indian or US number shows on your bill so we had to find something better.
For systems that require only sending we mostly used Singtel Ideas or Starhub Gee. Both are just a little short of and actual webservice, nevertheless worked quite well. Here's an example how to send message using StarHub Gee (12345678 is your phone number)
require 'rubygems' require 'mechanize' mech = WWW::Mechanize.new page = mech.get('http://groupsms.starhubgee.com.sg/VCRLS/login.cgi?cb=http%3A%2F%2Fgroupsms.starhubgee.com.sg%2Fcgi-bin%2Fs.pl') #login first form = page.form('loginform') form.action = "https://login.starhubgee.com.sg/eam/login" form['cb'] = 'http://groupsms.starhubgee.com.sg/cgi-bin/s.pl' form['vcid'] = 'groupsms' form['pudn'] = 'starhubgee' form['domain'] = 'starhubgee' form['fake_uid'] = '12345678' form['uid'] = '12345678@starhubgee' form['password'] = 'password' page = mech.submit(form) page = mech.get("http://groupsms.starhubgee.com.sg/cgi-bin/s.pl") form = page.form('compose') form['to']='comma separated recipients numbers' form['message']= "Your message...." page = mech.submit(form)
This would take care of sending with much lower cost as it utilizes bundled SMSes in the package (400 - 1000 messages) with nothing else required. The problem, however, was still receiving. A little glimpse of hope was SMS+ introduced by SingTel last year, which allows you to auto forward your received messages to email address. This would have been ideal - if not for a small glitch - it only works with messages sent from SingTel numbers. OH MY GOOOOOD!!!!!!!! I almost cried when I found out that.
Lately I found a nice and simple Singaporean provider www.gsmexchange.com able to handle 2 way communication. Nothing to say about sending - just send a http request with username, password, phone numbers and message and you will receive message ID that you can use for tracking. Replies are slightly more complicated as I didn't find any API for reading replies - nevertheless - there is a list page that you can parse with mechanize to get the responses. There is an option to forward messages to email but for 5c per message it's a bit too expensive. Actually, also pricing for sending of the messages is on the higher end 10c per message which makes it much less attractive for our customers.
Luckily I managed to find a much cheaper solution - running my own SMS server :-) See the next post...
Friday, April 3, 2009
In the Cloud
On of the biggest pains in the ass with web apps is not really the development, changing technologies nor extremely demanding customers. It's actually hosting - seems like the easiest thing yet it's something that, most of the times, you have only a very little control over. Of course you still can put the server in the office but if you have multiple locations you need pretty good connection to support it. I've written countless times about problems with hosting in Singapore - from crazy prices to the total lack of support and overselling of capacities. With pretty much the only "reasonable" hosting provider here (Frro) going down I found myself in the middle of yet another server migration. It seems easy but installation of around 10 servers can be quite daunting :-).
Luckily (I so hope this luckily is going to last at least some time), there's a new kid in town - Rack Space Cloud. There's been so much buzz around the clouds over the past year or so (Amazon, Google, Microsoft), but none of them was really useable for hosting of rails applications. Rackspace Cloud offers Cloud Servers - they're quite similar to Slice Host's slices, however, come with some "cloud" extras. I don't really understand the technicalities of the processor computing power sharing on the cloud and as (I would assume) they don't have that many customers yet I cannot say how it's going to affect the performance.
What's great about the cloud servers, however, is the flexibility of other resources together with hourly billing. And the prices are currently more than reasonable. For the cheapest set up - 256 RAM and 10GB will cost you 1.5c per hour + the band with 8c per incoming GB and 22c per outgoing GB. Most of my apps will rarely hit transfer of more than around 5GB so my total costs works out to around $12 - 13 per month. The same set up on Slice Host is even $20. But it's really not just a price comparison. One of the main strengths of the cloud is flexibility to resize the servers as you need - increase the resources during peak hours/days and shrink it back off peak. This could be nicely automated with the API for Cloud Server that is being developed. Personally, I have mostly used this flexibility to test and compare different configurations - mod_rails vs. Lighttpd, Postgresql per instance vs. shared, various Postgresql settings and all this with different memory sizes. This experimentation actually led to some surprising (for me) results.
Another great feature is back ups. They allow you to create new servers from the back up images. What it means is that I have images with my typical configurations (mod_rails, Lighttpd, Tomcat + Apache, database server, etc.) and when I need to add a new server I just choose the image I need and within a few minutes the server is ready with everything I need, and all I have to do is add the new IP to hosts and configure the DNS. As we manage over 50 different applications, this comes extremely handy - not just for adding new ones but also to keep the existing ones in sync.
No matter how great the initial set up, things will always go wrong so I was quite curious about the support. Rackspace has been know for their great customer service ("Fanatical Support"). As the control panel is very new (about 2 weeks) there are still some glitches, however, I found the live chat always available and ready to help. For the "bugs" in the control panel - the support seems to be automatically notified and fixes everything within minutes. How I wish this was the case with Singaporean hosting where my server can be down for hours without any response from support. Enough bitching. Over the years I've grown to be very skeptical about hosting (and internet) providers. They usually start great but as soon as I prepay for several months everything goes down. Let's hope this one's going to be different :-)