Sunday, December 13, 2009
Commodity Hardware
Saturday, November 28, 2009
Android
Thursday, November 26, 2009
Professional Certification
Sunday, November 15, 2009
Rehab
Friday, September 4, 2009
The Passionate Programmer in Business
Last week I wrote about The Passionate Programmer, about my and Chad's very similar ideas on many things. I closed by saying that it's hardly feasible or sustainable for any SME to base the business on passionate programmers. Imagine my surprise opening the PragPub Magazine yesterday where Chad Fowler talks about replacing your people with the worst people available. The idea is quite old and comes from Michael Gerber's The E-Myth Revisited. I read this book back in 2005 and I've been in internal fight with this book ever since. Even in my bookshelf it sits right next to it's antithesis Good to Great by Jim Collins. The idea Chad is getting at is that you should replace great people with great systems and get mediocre people to be run by those systems. Much like a clock - made of hundreds of small cogs and pieces, each easily replaceable and while totally insignificant alone, having a specific, clearly defined and essential role in the clock.
Let's backtrack a bit. I really loved The E-Myth Revisited book for the questions it asks. In fact, a bigger part of the book is dedicated to depicting a typical small business. Painstakingly describing day after day with such a detail and accuracy - I don't know a business owner who wouldn't resonate with every page. Unfortunately, it leaves out most of the answers. Actually, the answers are there, but while you can really feel the pain of the questions, the answers are put almost bluntly - like an external consultant just took over and reads his 6 steps to happiness. It took 10 years for the real answers to arrive. They're in the E-Myth Mastery. While this book never neared the popularity of the first one, I found it infinitely more useful. The main difference is that while the questions remained, the second book provides actual answers. While the first one is based on observation and consultancy, the second is based on first hand experience and the author lives through every sentence. I bought the book in 2006 but I didn't get round to reading it until late 2008. And it was a deja-vu. I read maybe 50 books over those 2 years - yet I didn't read the one that could/would have actually helped me. It was like somebody wrote a book about the what was going to happen, but I couldn't read it because I was too busy heading for the catastrophes I could have avoided if I just read the damn book.
Now back to the good vs. bad people question. Our business is streamlining and automating of business processes. Over the years we worked with companies from more than 30 different industries. Anything from travel agents, through media, manufacturing, logistics to financial consultants and portfolio management. People usually get our systems to either decrease their staff numbers while increasing performance or keep the numbers and radically improve performance. The way to do it is based on what Michael Gerber says - take a process and orchestrate it. In our case, it's followed by computerization, where the computer system does the heavy lifting. In Michael Gerber's case, once the system is orchestrated, the cogs (aka the least qualified people you can find) can be put in places and the show is ready to go on. Your role as a mastermind, is to grease the cogs, innovate and move from technician to manager (and later to businessman). This has worked remarkably well - just look around - this is the principle behind every McDonald's, Burger King, Starbucks, Subway, and so on. As a freshman in university I applied for job in McDonald's but at the end chose to work in research institute. I regret that decision to this very day. While I made considerably more money working in the research institute I lost opportunity to learn about how one of the very successful businesses is run. On the other hand, I've seen a number of places where just systemizing didn't work all that well. Take accounting as an example. The process is well defined (even though with considerable amount of leeway in asian countries), there are formal checks in place like audit, tax checks and so on. Yet I've seen it in quite a mess in a number of places. While there are better accountants just like there are not so great accountants most of them are able to do accounting. The cause was usually somewhere else. Most of the business owners are masters of their trade, they are the masterminds behind the systems and they are the puppet masters running the operation. When it comes to accounting, however, they're lost. So they hire an accountant. And then another. And data entry clerk. And temp staff because of the back log. And still the accounts are in mess. Buying a better (more expensive) accounting system doesn't help much neither. What's missing is usually leadership. It's not an accountant the technician but accountant the puppet master. Human systems don't tend to be as iron clad as the clockwork or jet engine. That's where the 1 obsession of the extraordinary executive (build and maintain a cohesive leadership team) comes from. You cannot be everything to everybody and people are not cogs - you can't just put them in places and expect them to function flawlessly.
I've always believed that you get only as good people as you deserve. And I've paid the price to learn this again and again and again. Most of the work I've been around is not linear. You can hardly say that if one CEO produces 1 mil of profits five CEOs would turn the same company in 5 mil business. Just like if a system takes 1 developer 3 months to develop it doesn't mean it would take 3 developers (offshore or not) 1 month. Just like if one sales person can close deals worth 100k per month it doesn't mean 3 of them will close 300k.
The Chad's article goes on to explain business automation likening it to automation of software development. While interesting to read - this is how software developers must feel when being compared to construction workers :-). I guess (literally guess) that Chad has passed the euphoric stage in his business and tries to see where is all that going. I wouldn't be surprised if it looked awful lot like working in a big company he left, but with way more responsibilities and way less money. That's where I went through the same Freedom musings. Hopefully, I am wrong and the wine and roses will last forever. They didn't for me, though. Time came when I realized that my business was just a business - without the magic IT formula from The 50 Great E-Businesses and instead of counting millions or billions on my account I had (and still have to) change from passionate programmer passionately pursuing his own sweet career to someone running the business. It's not just about me anymore...
Anyway, today I found a book that does a great job explaining just how different running a business is from software development. The book is The 100 Best Business Books of All Time. I am not saying that reading this book or even all 100 books listed will make you the best business person of the century or that it will solve all your problems. I merely believe that reading some of them will help you see what's on the other side and consider it before it's too late. There's a saying that smart people learn from mistakes of others...Never happened to me, but I hear it's great :-)
Friday, August 28, 2009
Passionate Programmer
It's really been some time since I wrote something. I've been really busy - more than usual busy with all the projects but additionally busy trying to workout a solution to some of my perennial problems.
I finally managed to read The Passionate Programmer. It almost felt like reading my own notes. Over the past few years I've been talking and writing about quite a number of issues from this book. It's almost unbelievable that somebody has so similar views. Starting with the saxophone experience - I played saxophone for 10 years - I can relate really well to Chad's stories and parallels. Of course, that's not to say that because some things work in music they will work in programming as well. As Martin Fowler once said software development is software development and we shouldn't be comparing ourselves to other industries and drawing misleading or wrong parallels. Anyway, regardless of whether the conclusions are right or wrong the flashbacks to saxophone playing experience added extra level of depth (at least for me).
Out of the many ideas that struck a cord with me...here are a few I noted down
Invest in Learning
The nerve!!! Judging by the reactions I receive whenever I talk about importance of proper education and practice for programmers - it's ranking among the worst insults you can ever make. Every programmer worth his salt knows that learning is for dummies. We were born better than them (basically everybody else). Sarcasm aside, I believe that a lot of programmers would benefit from experience of playing a musical instrument. Daily (i.e. every day) practice and performances. When you're performing on stage without practice - okay let me rephrase that - when you're on stage without hundreds of hours of practice - you're very likely to screw up - and if you do you're like naked. There's nowhere to hide. The whole audience looking at you and everybody knows it was you who screwed up. Believe me, the next time you'll think more than twice before coming on stage without proper practice. When you program without practice (or learn on the job) you're just as likely to screw up...the only difference is that you can blame it on the customer, on the specs, on the technology, or whatever or whoever else around you. Many times you can just comfortably hide behind your boss as he/she is the one facing the customer.
Work expands so as to fill the time available for its completion
This is an interesting concept. In the industry where everybody complains about way too tight deadlines - Chad goes on to say that just by making the deadlines even tighter you can easily double or even triple your productivity. While I am still to meet an employee that would confirm this - it's definitely true for me and all my ex-staff that went independent. It's really fun to see that things that took them several months to finish while being employed can suddenly be done in a month when they're on their own.
Writing Skills
"If you can’t organize your thoughts in your mother tongue so that others can clearly understand them, how can we expect that you can do it in a programming language? The ability to shape an idea and lead a reader through a thought process to a logical conclusion is not much different from the ability to create a clear design and system implementation that future maintainers will be able to understand." While this politically incorrect statement would get you to prison in a number of countries it's surprisingly true. And quite opposite to the geek stereotype. Interestingly enough, ability to speak and organize thoughts clearly has become a very important factor for us. Try to notice the similarities between way people speak and code - if you have a programmer that is very verbose - talking, talking, talking and still not making the point clear - chances are his code will be very similar - long winded, using unnecessary constructs and never quite clear on what it's supposed to do. On the other hand, if you have an introverted programmer that says 10 words a day, chances are his code will be very terse, taking one liners to the whole new level.
Businesses and those who run them are interested in business results
A thing you learn really fast when you're on your own but it's your biggest enemy when you're an employee. I remember a fight I had with one of my employees - his conclusion was that he just wanted to have a life. And he was right. Why should he worry about the deadlines or delivering what the customers needed? His goal was to have a life and the work was one of the main constrains - there was no direct link between his performance and quality of life. Not seeing and understanding this was my biggest failure running the business earlier on. While we had the basic performance based structure in place it was so detached from the company's culture and pretty much left for everybody to make their own interpretation. The challenge is to reorganize from a black box to a concert stage where everybody is crystal clear on how his performance is linked to the achievements of the whole team and how that translates to the distribution of rewards. Chad puts it very bluntly: "Sure, you got it done, but what was it? Why did it matter? How was this so-called accomplishment not just a waste of company time?" He couldn't be more right. I cannot even count how many times people told me that they had been making sacrifices, sitting in the office 8 or 9 hours every day and I still complain about their performance. The issue here was that while they counted hours I was counting closed iterations and customer satisfaction.
It's easy to get hung up on technology choices
Especially when our technology of choice is the underdog... I believe this idea comes from the Pragmatic Programmer - something along the lines of when all you have is a hammer everything starts to look like a nail. I've always tried not to be attached to technology, however, having a 50 projects investment in a technology makes you very close to being dependent on whatever choices you made. Yet, an investment in wrong technology - no matter how much you like it is usually much higher than investment in a different technology that is actually right for the job.
Go independent
This is quite a dangerous point. While definitely true - there are uncountable advantages to running your own show - just go for any motivational seminar by T. Harv Eker, Robert Kiyosaki and the likes - but being the best technician is only a start. Even though, based on the book 50 Great eBusinesses there's a number of teenagers without any experience that are managing billion dollar businesses (or one man shows) out of they're parents living rooms - vast majority of businesses and people are not so lucky. While Chad describes this as natural next step and spends only a few pages explaining the benefits and some of the obvious challenges it's a whole new ball game - one that has very little to do with programming.
Finally, the book got me really thinking. I've been running the software business for 7 years now and while we've been fighting with lots of problems, the single most difficult thing has been to get the right developers. I wrote about different aspects of it in a number of posts analyzing it from many different angles. This books is/says exactly what I've been trying to say and find for all this time. So many people told me that my ideal developer is a mix of too many and too different skills, and that such animals are more rare than a unicorn. On one hand it was very encouraging to see that somebody else has the same ideas and expectations, on the other it's quite a gamble to base your business on something so rare. Yes, it's possible to find one or two and get started. It may even be feasible to hack a new Web 4.5 killer app and sell it to Google for millions of dollars, but it may be much harder create a sustainable business out of it. It may work for huge multinationals that Chad is talking about as they can afford $300,000 packages but hardly any SME can do that. In fact, I couldn't sleep for almost 2 weeks trying to find a solution questioning my every belief and experience with this business. I tried to unhung my self from the technology, the product, the people and try to weigh the price and the risk of each option today and 10 years from now. It's been quite an exercise - I am now iteratively implementing the changes - if at least 25% of it works out this could be a book that totally changed my business and life. We'll see in half a year :-)
Sunday, July 19, 2009
Ext JS Image Field
Ext.form.ImageField = Ext.extend(Ext.form.Field, { autoCreate: {tag: 'img'} ,setValue: function(new_value){ if (new_value == undefined || new_value == null) { this.el.dom.src = '/images/no_image.png'; } else { this.el.dom.src = '/images/thumbnail/' + new_value; } } ,initValue : function(){ this.setValue(this.value); } ,initComponent: function() { Ext.apply(this, { }); Ext.form.ImageField.superclass.initComponent.apply(this); } }); Ext.reg('image_field', Ext.form.ImageField);
line 2 creates image tag (rather than the default text field tag)
line 5 defaults the image src property to a placeholder (in case there's no image uploaded)
line 7 - in case there is a valid image it will change the src path to that image - in my case it's routed to Images controller and action thumbnail which returns a downscaled version of the image.
def update contact = Contact.find(params[:id]) on_bind(contact) success = contact.save contact = Contact.find(contact.id) if success logger.info("saved") respond_to do |format| format.ext_json do json = contact.to_ext_json(:success => success, :methods => [:image_id], :includes => [:address], :messages => {:notice => l(:contact_updated), :warning => l(:contact_update_error)}) render :json => json #:json => {:success => success} headers["Content-Type" ] = "text/html" end end end
{"success": true, "messages": {"notice": "Contact was successfully updated.", "warning": "contact_update_error"}, "data": {"contact[image_id]": 15, "contact[active]": false, "contact[client_account_id]": 2, "contact[client_account_no]": "100", "contact[created_at]": "2009-07-07T10:31:54+08:00", "contact[credit_term_id]": 1}}Now in the forms success method I can use the result.data to update the form:
success: function(form, action) { form.setValues(action.result.data); },
Friday, July 17, 2009
Losing Faith
- I am looking for a modular framework where I can choose different ORM, different templating engine and so on where each component is developed independently by people who understand that specific area. As each of my project is very different sometimes I need the flexibility to change components. It was a great thing back in my Java times (Spring + Hibernate + Webwork).
- I really need an enterprise friendly dynamic language. Life's just too short to be wasting time trying to force a language to do things it's inventors don't consider important.
- I need a basic ecosystem of libraries (from XML, JSON, through excel, PDF, OO to image manipulation).
- I need a framework that supports TDD out of the box.
- I need a platform with (at least some) formal education and certification available. I need it because I employ people and certification really helped me back in Java times. To pass even the first Java certificate required people to study and to really understand the language and they simply took it as an independent measure of their skills.
- I need a language with a proper IDE. I use Vim every day - BUT not for programming. I've been using RubyMine for more than a month now and I feel like a blind man that got his eye sight back.
- I need reasonable deployment option - both scalable and resource efficient.
- Increasingly more important - I don't want the language/platform community to offend others
Monday, July 13, 2009
Extracting Data From MS Sql Server on MacOS
Restore database from backup
You will need a windows machine with MS Sql Server running (of course :-) Luckily, you can download it for free from here. Next connect to the database server using the command line utility osql (you will need to have it in your PATH)cd C:\Program Files\Microsoft SQL Server\MSSQL\Binn osql -EYou may need to create a new user:
use master go EXEC sp_addlogin 'peter', 'pass45' goNow let's have a look at the backup file (put it in some easy location so you don't have to type out the path):
Restore FILELISTONLY FROM DISK='c:\AMS.bak'This query allows us to find out the logical name of the database and log file which is needed to appropriately restore a database to a new path. What you get is basically an original location of the data and the log file. In my case I got:
AMS_Data D:\Program Files\Microsoft SQL Server 2000\MSSQL\data\AMS_Data.MDF AMS_Log D:\Program Files\Microsoft SQL Server 2000\MSSQL\data\AMS_Log.LDFWhich means that the original installation was on drive D:\. As my temp server is on C: I will have to recover with changing the locations of the files
RESTORE DATABASE ams FROM DISK='c:\AMS.bak' WITH MOVE 'AMS_Data' to 'C:\Program Files\Microsoft SQL Server\MSSQL\Data\ams.mdf', MOVE 'AMS_Log' to 'C:\Program Files\Microsoft SQL Server\MSSQL\Data\ams_log.ldf' goThe only important thing here is that 'AMS_Data' and 'AMS_Log' names match the ones from the previous query. Now you should hopefully see that your database has been restored. Now, will just get access to the database:
use ams go EXEC sp_grantdbaccess 'peter' go sp_addrolemember db_owner,peter goThere's more info on how to use osql here. Now we should be set to connect to the database from development machine. This was quite a number of steps just to restore the database - compared to createdb tmp; psql tmp <>Connecting to MS Sql from MacOS We will need to install and set up a couple of things:
sudo port install rb-dbi +dbd_odbc sudo port install freetdsset up connection to database
/opt/local/etc/freetds/freetds.confAdd your server to the end of the file:
[Ams] host = 192.168.11.106 port = 1433 tds version = 8.0where the host is IP of your windows machine. After this step you should be already able to connect to MS Sql Server:
tsql -S Ams -U peter -P pass45You should get the server database prompt (just like the osql on windows machine). You can try some commands like
use ams go select * from contacts goNow set up ODBC connection. Go to Applications -> Utils -> ODBC Administrator 1) add driver with following descriptions:
Description: TDS Driver File: /opt/local/lib/libtdsobdc.so Setup FIle: /opt/local/lib/libtdsobdc.so Define as: System2) add User DNS DSN: Ams Desc: old AMS database server add 2 keyword/value placeholders. To update them click on it and press TAB. Set it to following values:
ServerName: Ams (or whatever you set in freetds.conf) Database: ams (or whatever your database name)now you should be able to test iODBC. Note that I am using sudo for this as it doesn't seem to work without sudo complaining Data source name not found and no default driver specified. Driver could not be loaded (0) SQLSTATE=IM002.
sudo iodbctest "dsn=Ams;uid=USERNAME;pwd=PASSWORD"You should be now in interactive terminal again. Once all this works connecting from Ruby is really easy:
require 'rubygems' require 'dbi' DBI.connect('DBI:ODBC:ams', 'USERNAME', 'PASSWORD')
Rescuing the data
This is just a short ruby script I use to extract all the data from MS Sql and import it to Postgresql. It's not any functional database conversion - it's just to get the data out to something that's easier to work with and doesn't require windows machine running. It may have an issue with binary fields - it worked on most of them but it did choke on a couple of fields. DBI actually provides more metadata like NOT NULL attribute, primary key, etc. so the script could generate a more precise copy of the original database but this was enough for what I needed. You may run into unknown data type - especially if you try to import it to a different database engine - all you need to do is just update the method update_type to return correct mappings of the data types.require 'rubygems' require 'dbi' require_library_or_gem 'pg' def escape(text) return "NULL" if text.nil? text = PGconn.escape("#{text}") return "'#{text}'" end def self.update_type(col) type = col["type_name"] type ||= 'timestamptz' type = type.downcase case type when 'clob' return 'varchar' when 'varbinary' return "oid" when 'long varbinary' return "oid" when 'double' return 'double precision' when 'tinyint' return 'smallint' when 'char' return "char(#{col['precision']})" else return type end end dbh = DBI.connect("DBI:ODBC:ams", "peter", "pass45") sth = dbh.prepare('select name from sysobjects where type = \'U\' ORDER BY NAME;') sth.execute tables = sth.collect{|row| row[0]} tables.each do |table| sth = dbh.prepare("select * from #{table} ") sth.execute create = "CREATE TABLE #{table}(\n" create << sth.column_info.collect{|col| "\"#{col['name'].downcase}\" #{update_type(col)}"}.join(",\n") create << ");\n\n" puts create sth.each do |row| create << "INSERT INTO #{table} (#{sth.column_info.collect{|column| "\"#{column['name'].downcase}\""}.join(', ')}) VALUES (#{sth.column_info.collect{|col| escape(row[col['name']])}.join(', ')});\n" end create << "\n\n" output = File.new("data_ams.sql", "ab+") output.puts create output.close #puts create endTo import the data to Postgresql is as simple as:
createdb ams_backup psql ams_backup <> noise.txt
Friday, July 3, 2009
Software Development as a Profession?
Friday, June 26, 2009
Software Development is Hard
Friday, June 19, 2009
The Talent Code
I read an interesting book couple of weeks ago. The Talent Code by Daniel Coyle. While the whole book is really good I particularly liked the section explaining the "sudden" appearance of extraordinary talents - like Brazilian football players, Bronte's sisters, Mozart, venetian sculptors and painters and so on. How it's believed that all those people were simply born with great talent and their great work just happened. And how the reality was almost a total opposite. The main idea behind the book is that no matter what you do, in order to achieve a world class skill you need to spend 10,000 hours in practice. The skill is basically "neural connections" in the brain. Those connections are created during practice but what determines the level of the skill is the "quality" of those connections. What increases the quality of the connection is myelin by insulating nerve fibers. The 10,000 hours is necessary to create sufficient myelin. What's interesting is that it's not just any practice - the best results are achieved in "deep practice".
The author goes on to explain how any of the great talents already had their 10,000 hours clocked in by the time they were "discovered". How bronte's sisters had written tens of training books before Jane Eyre or Wuthering Heights, how Mozart had his 10,000 hours of practice in very early age and how venetian painters and sculptors got their hours of training in apprenticeship. They were not born great but started from scratch and achieved their greatness by increasingly improving their skills. Interesting thing is that the apprenticeship didn't mean that they would simply paint the whole day and after 10 years became masters. No - they had to do all sorts of things - especially all sorts of "low level things" - like setting the canvas, preparing chisels and so on for their masters. Only after they learnt the basics could they move on to more difficult things. And this is what basically constitutes the "deep training" - choose a goal just outside your comfort zone and keep on failing until you achieve it. Then repeat the process.
To sidetrack a bit to IT - this is where I think today's system breaks down. Software development is hard. And it only starts with the technical skills - you still need to understand the domain (when working on accounting system you have to have a very solid understanding of accounting), you have to be able to work with people and translate their ideas and feelings about something they've never seen to reality. And there is no magic shortcut - you have to have your 10,000 hours clocked in before you can really do something. Programming is like writing - just like bronte's sisters - you have to write a lot a lot a lot and about everything you can find - only then your writing will start making sense. Unfortunately, I wouldn't really count any hours spent at school. Maybe it's an unfortunate situation around here - but the IT schools here are set to producing CIO's making decisions on a golf course rather than doing programing or any other actual IT work. To their defense that's pretty much what market here wants - most of the fresh grads from IT will find a good shake-leg work in a bank or MNC paying at least $10,000 per month. I meet quite a few of those people in my trainings or recently, as the banks scale down certain areas, in interviews.
Anyway, my point here was that only very very few people in IT are willing (or forced) to go through the whole journey - from setting up networks, installing computers, moving to servers, maintaining different operating systems, doing backup / recovery, working with databases, optimizing databases, writing SQL, programming in several languages for several years, understanding patterns going through several releases and so on. And none of that means "I used MS Access in 3rd semester".
Another interesting thing was the difference between masters and beginners. The difference was explained on chess players. The difference between chess masters and beginners is that masters can remember the whole board set up on one look. There is a twist, though. They could only remember the set up from an actual game. If the figures were in random order the chess masters photographic memory vanished. The reason why chess masters could remember the board setup from actual games is because they were not seeing the individual pieces - they were seeing the patterns.
I picked up the book because I wanted to know more about how people learn - I wanted to understand why after a year of training and working some of my employees are not able to do even a simple task, why is it that they spent a week installing a server without any success while somebody else can do the same work in 1 hour. Why some spend months and months programming a piece of functionality creating a total mess and somehow not "seeing" the simple solution that can be finished in a few hours or days. I used to be especially puzzled about the not seeing part. Even after showing them the simple solution they just couldn't understand it and had to go a big round of all possible wrong ways to arrive at the same solution. I guess some of them see patterns while others see thousands of lines of commands. As a result, in the light of the book's 10,000 hours I start to look very differently at this kind of things. The good news is: it's learnable. The bad news is: it may take 10 years for someone to learn it.
Saturday, May 9, 2009
Rails 2.3 with Ruby 1.9
incompatible character encodings: UTF-8 and ASCII-8BITwhen data from database contain special characters. There are several patches available in lighthouse here and here but non of them really worked for me - safe for
contact.name.force_enconding('utf-8')4) send_data doesn't work on binary files with message
invalid byte sequence in UTF-8. I used to use it for on the fly generated images but had to replace it with creating a temp file and then use
send_file
Friday, May 1, 2009
File Downloads in Rails on Apache
Over the past half a year we worked on several systems for logistics/courier/delivery companies. It was quite fun as a big logistics and inventory control system was my very first project when I arrived to Asia 9 years ago. Last week we launched one of them. It was slightly different as most of our systems are used mainly internally but this one is also used by clients of the courier company to book deliveries. Each delivery requires printing of confirmation documents, internal routing documents, then signed and stamped documents are uploaded back to the system as a proof of delivery for clients. It has quite a number of other (technically) interesting features like integration with google maps to geo-tag the orders and notifying drivers in the area - I'll write about it soon.
As this courier company handles several hundreds of deliveries every day it requires quite a number of uploads/downloads of documents and images. The issue with uploads and downloads is that they will tie up rails processes for much longer than usual requests. The problem is that rails processes are quite expensive in terms of memory (around 70 - 100MB for simple operations). Do 20 concurrent uploads/downloads and all your rails processes and memory will get tied up - and quite wastefully as there is no 'dynamic' processing during upload or download. The process just sits there and waits for the file to get delivered. Luckily there's a simple way how to move this burden to Apache. This kind of upload/download processes will take only around 2MB on average and your rails processes are free to handle the dynamic requests.
To let apache handle file downloads use x_sendfile. All you need to do is install x_senfile module on apache and adjust you config a little. Here's the installation instructions for Ubuntu 8.04:cd /usr/local/src wget http://tn123.ath.cx/mod_xsendfile/mod_xsendfile.c apxs2 -cia mod_xsendfile.cOpen apache config file and add the module line:
sudo vim /etc/apache2/apache2.conf
LoadModule xsendfile_module /usr/lib/apache2/modules/mod_xsendfile.soAdd the following 2 lines to your virtual host file
sudo vim /etc/apache2/sites-available/my_app
XSendfile on XSendFileAllowAbove onYou will have to restart the apache and you should be done:
sudo /etc/init.d/apache2 restartNow in your controller handling download add x_sendfile attribute and set it to true
send_file("/home/test/big_file.zip", :filename => "big_file.zip", :type => "application/zip, :x_sendfile=> true)The uploads are equally easy. You just need to apache modules libapreq2 and modporter.
wget http://mirror.nus.edu.sg/apache/httpd/libapreq/libapreq2-2.12.tar.gz tar -xzf libapreq2-2.12.tar.gz cd libapreq2-2.12 ./configure --with-apache2-apxs=/usr/bin/apxs2 make sudo make installNow download and install modporter. Get it from the github. Either use the download link and unzip or just clone the repository
git clone git://github.com/actionrails/modporter.git cd modporterOn Ubuntu I had to change reference to apxs in Rakefile as the in ubuntu it's apxs2.
vim RakefileChange the APXS line to the following:
APXS = "apxs2"No just run rake:
sudo rakeadd the following to your apache2.conf
sudo vim /etc/apache2/apache2.conf
LoadModule apreq_module /usr/lib/apache2/modules/mod_apreq2.so LoadModule porter_module /usr/lib/apache2/modules/mod_porter.soTo actually use it in your rails application you will need modporter plugin.
script/plugin install git://github.com/actionrails/modporter-plugin.gitAdd the following to the application_controller.rb (application.rb before Rails 2.3)
class ApplicationController < ActionController::Base mod_porter_secret = "secret" . .and just enable porter in you vhost file:
sudo vim /etc/apache2/sites-available/my_app
Porter On PorterSharedSecret secretAnd that's all now if you check the uploaded files they will be a ModPorter::UploadedFile Credit for this goes to Koz from theRailsWay.com - you can find more information here.
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 :-(