Getting readline to work with Ruby Version Manager (RVM) on Linux Mint KDE
I couldn't get readline to be picked up using the 'rvm package install readline' command. So instead I had to install the readline development headers as a Debian package
sudo aptitude install libreadline6-dev
And then tell rvm about the /usr directory when installing ruby
(Note: I had run 'rvm package install zlib' and 'rvm package install openssl' before running this next command)
rvm install 1.8.7 -C --with-zlib-dir=$HOME/.rvm/usr --with-openssl-dir=$HOME/.rvm/usr --with-readline-dir=/usr
This solution should also work for Ubuntu.
Accessing your encrypted home directory in Ubuntu
This process details accessing an home directory, which was encrypted using the "encrypted ~/Private directory" technique, that has been with us since Ubuntu 8.10 (Intrepid Ibex) and available as an install option.
This took a lot longer than expected in terms of sifting through information. Here I detailed what worked for me. I used the guide on Recovering your Data Manually as the foundation.
Note: I tried to carry out this process using OpenSuse 11.2 but was unsuccessful. Here's some notes regarding my OpenSuse attempt
- The package name of the encryption utility is the same on OpenSuse 11.2, so you can use
zypper install ecryptfs-utils - The syntax of the
ecryptfs-add-passphraseseemed to be different as it didn't have a--fnekoption - I suspect the problem is that the ecryptfs utility that comes with OpenSuse 11.2 doesn't support file name encryption. I found that I could decrypt the files and directories, view their contents but the file and directory names were all mangled
Ok, meanwhile back at the ranch, we're trying to access an encrypted Ubuntu home directory from another partition, in my case a Kubuntu installation. When reading this guide be aware that there are two passphrases mentioned - a login passphrase (the usual password you use to login to your computer) and a mount passphrase (a big long jumble of characters that is vital to decrypting your partition). Later in the guide we also mention the sudo password - this is the password you type to get full superuser privileges on your machine. Here we go...
Step 1: Getting the all-important mount passphrase
First, we need the mount passphrase that was generated when you first installed Ubuntu with the directory encrypted (or whenever you manually encrypted the directory, if you didn't do so during installation). If you were diligent at time of encryption you have written this down somewhere. For the rest of us
, log into the Ubuntu containing the encrypted home directory and run the command,
ecryptfs-unwrap-passphrase /home/.ecryptfs/ubuntu_user/.ecryptfs/wrapped-passphrase
Then type in your login passphrase (it's the one that you usually type to login into your computer) when prompted in order to reveal the mount passphrase (the big jumble of characters). Record the mount passphrase somewhere as you'll need it later.
I can't help you if you don't have your the mount passphrase or are unable to log into the Ubuntu containing the encrypted home directory which you are trying to access.
Step 2: Accessing the encrypted home directory from another O.S.
Log into the other O.S., as I said, in my case a Kubuntu installation.
- Ensure that you have the encryption utilities installed
sudo apt-get install ecryptfs-utils
- Browse to the encrypted home directory on the other partition using your file manager in order to mount it. I used Dolphin on Kubuntu to browse to
/media/disk-3/home- If you have found the correct directory you should see 2 files
Access-Your-Private-Data.desktopandREADME.txt - We will need this directory later
- If you have found the correct directory you should see 2 files
- From the command line, navigate to your normal home directory on Kubuntu
cd/home/kubuntu_user
- Create a directory in which we can mount the encrypted home directory
mkdir OtherHome
- We need to get a special signature that will be used later for decrypting filenames (you can skip this step if you are trying to access Ubuntu 8.10 or less I think)
sudo ecryptfs-add-passphrase --fnek- (note: first you will need to enter your sudo password and then the mount passphrase)
- Pay attention to the second
"Inserted auth tok with sig"line and note down the value in square brackets (eg. 66a9f67af69a86aa) as we will need this signature later
- Here's the real magic, which decrypts the home directory on Ubuntu and accesses it via the OtherHome directory on Kubuntu.
Note: Carefully look at the command and change the/media/disk-3/home/.ecryptfs/ubuntu_user/.Private/bit to whatever is correct for yousudo mount -t ecryptfs /media/disk-3/home/.ecryptfs/ubuntu_user/.Private/ /home/kubuntu_user/OtherHome- You will be asked a series of questions
- Select passphrase as the key type
- Enter the mount phrase when asked for the passphrase
- Select aes as the ecryption cipher
- Select 16 bytes as the key length
- Enter n for enabling of plaintext passthrough
- Enter y for filename encryption (if you obtained the special signature in the earlier step)
- Enter the special signature from earlier when you are prompted for the Filename Encryption Key (FNEK) Signature
- Note: You will get the following warning but there is no need to worry about it
- WARNING: Based on the contents of [/root/.ecryptfs/sig-cache.txt],
it looks like you have never mounted with this key
before. This could mean that you have typed your
passphrase wrong
- WARNING: Based on the contents of [/root/.ecryptfs/sig-cache.txt],
- If it worked then the very last line will be
Mounted eCryptfs
You should now be able to navigate into OtherHome and access the files there. Hurrah!
Beginning Optimization in MySQL
The first place to start be to run your query using EXPLAIN in order to see what MySQL execution plan. Simply put EXPLAIN in front of your SELECT statement and check the output. Initially, you might think that its output doesn't give hard figures and as a result is pretty awkward to understand. But you'll soon see that it really helps you understand your queries and how they interact with things like indexes. The first page of this article by Ian Gilfillan is a great explanation of explain (bit of a mouthful that!).
The other good starting point is to known how does MySQL do joins? Well, the short answer is the single-sweep multi-join method and if you didn't know that then check out this post by Mike Papageorge.
Cache rich, time poor...
Of course, as you use EXPLAIN you'll try different things in your query and re-run queries to see the difference in performance. You'll want to some hard figures. Each time you run a MySQL query from the MySQL command line, or MySQL Query Browser, it will give you a query execution time. Unfortunately, the second time you run a query the execution time will be drastically shorter - because the results from the previous execution will be in MySQL's Query Cache. The only reliable way I've found to minimise the effect of the cache is to restart the MySQL server after every query (eg. /etc/init.d/mysql restart). It won't give you the exact same metric as, for example, after a reboot of your machine but it's a good line in the sand nonetheless.
Ok, a quick checklist...
The thing is someone else has always done the hard work for you on topics like this. A great place to start is with this checklist by Sevn Welzel. Here's my favourite from that checklist, which I'm listing here more as a memo-to-self than anything else.
Note: That MySQL 5.0 introduced a lot of changes so certain optimizations pre-5.0, such as converting OR statements to UNIONs are no longer needed.
- Derived tables (subqueries in the FROM clause) can be useful for retrieving BLOBs without sorting them. (Self-join can speed up a query if 1st part finds the IDs and uses then to fetch the rest)
- Avoid using IN(…) when selecting on indexed fields
- InnoDB ALWAYS keeps the primary key as part of each index, so do not make the primary key very large
- This list of Easy MySQL Performance Tweaks
- Sven also lists a bundle of tips by Alexander Skakunov at AjaxLine the most useful one for me being the way the MySQL Query Optimizer can use the leftmost index prefix - this mean you can define index on several columns so that left part of that index can be used a separate one so that you need less indices (though remember that your index will be bigger overall and hence not a fast to search as a smaller index)
Handpicking Indexes
A big part of optimizing in MySQL is experimenting with different indexes on your database but you don't want to have to keep adding and removing them - as this can take a long time on big tables. Consider using IGNORE INDEX(some_index) if you'd like to see how your query would perform in the absence of an index or FORCE INDEX(some_index) if you'd like make sure MySQL to use's a particular index. By looking a the output of EXPLAIN you can see which indexes are being used. Sometimes, if you know your data well, you can outsmart the MySQL query optimizer. Though be careful when doing so as if the shape of your data changes your optimization might work against you. More details at MySQL Manual Index-hints.
Top optimization picks from the MySQL Documentation
The MySQL docs really are great at explaining a lot of this stuff and the optimization sections are well worth a read. Here I've just selected the bits that were the most useful as I worked through this area and expanded on them in one or two cases.
- From MySQL SELECT Documentation
- STRAIGHT_JOIN forces the optimizer to join the tables in the order in which they are listed in the FROM clause.
- The Irish Penguin says: "This can be really really handy. For example, if you have a GROUP BY clause that references a column in a large table in a query featuring some JOINS, you probably will want to the table containing this column to be the first hit in the query - even if it produces a much higher number of rows examined figure in the EXPLAIN output. My understanding is that the way MySQL decides on its query plan is to put the table that is likely to yield the fewest rows first. But this can be at loggerheads with GROUP BY clauses. It does a similar behaviour when choosing indexes as this MySQL Performance Blog article states"
- From MySQL INDEX and MULTIPLE-COLUMN INDEX
- If the table has a multiple-column index, any leftmost prefix of the index can be used by the optimizer to find rows. For example, if you have a three-column index on (col1, col2, col3), you have indexed search capabilities on (col1), (col1, col2), and (col1, col2, col3).
- If a multiple-column index exists on col1 and col2, the appropriate rows can be fetched directly. If separate single-column indexes exist on col1 and col2, the optimizer will attempt to use the Index Merge optimization (see Section 7.3.1.4, “Index Merge Optimization”), or attempt to find the most restrictive index by deciding which index finds fewer rows and using that index to fetch the rows.
-
- If a range scan is possible on some key, the optimizer will not consider using Index Merge Union or Index Merge Sort-Union algorithms
- Consider running OPTIMIZE TABLE if you have deleted a large part of a table or if you have made many changes to a table with variable-length rows
- From MySQL Optimizer Issues
- Use ANALYZE TABLE tbl_name to update the key distributions for the scanned table. See ANALYZE TABLE Syntax
- From InnoDB restrictions and InnoDB turning
- An InnoDB table cannot contain more than 1000 columns
- SHOW TABLE STATUS does not give accurate statistics on InnoDB tables, except for the physical size reserved by the table. The row count is only a rough estimate used in SQL optimization
- Cascaded foreign key actions to not activate triggers
- InnoDB does not store an index cardinality value in its tables. Instead, InnoDB computes a cardinality for a table the first time it accesses it after startup. With a large number of tables, this might take significant time. It is the initial table open operation that is important, so to “warm up” a table for later use, access it immediately after startup by issuing a statement such as SELECT 1 FROM tbl_name LIMIT 1.
- Relating to the Group By Optimization
- Usually a
GROUP BYclause causes a scan of the whole table and the creation of a new temporary table where all rows from each group are consecutive. This temporary table is then used to establish groups and apply aggregate functions. MySQL can sometimes use indexes that avoid temporary table creation if the query is written in a certain way - allGROUP BYcolumns must reference attributes from the same index. Also the index must stores its keys in order (for example, this is aBTREEindex and not aHASHindex). See this InformIT article for the difference between BTEE and HASH indexes and more information - Very importantly, make sure that the column(s) involved in the GROUP BY clause are from the lead table in your query. If MySQL's Optimizer is choosing a different table as the lead table try using STRAIGHT_JOIN to force MySQL to first hit the table containing the columns that appear in the GROUP BY when executing the query. Why doesn't MySQL always do this by default? MySQL's Query Optimizer prefers to using indexes for restriction (WHERE clause) rather than sorting (GROUP BY or ORDER BY) that's why you sometimes need to give it this hint
- Usually a
- Relating to the Limit Optimization
- The LIMIT clause can speed up things even when applied after an ORDER BY clause. MySQL does not always need to finish the sorting before it can apply the limit. In some cases, you can think of LIMIT as a buffer that MySQL fills up as it sorts the results - MySQL stops as soon as the buffer is filled. For example, if you use LIMIT 10 with ORDER BY, MySQL ends the sorting as soon as it has found the first 10 rows of the sorted result, rather than sorting the entire result. Here's some caveats
- Avoid using a HAVING clause as this will prevent the optimizations
- You really want the ordering to use an index. Otherwise if a filesort must be done, all rows that match the query without the LIMIT clause must be selected, and most or all of them must be sorted, before it can be ascertained that the first 10 rows have been found
- The LIMIT clause can speed up things even when applied after an ORDER BY clause. MySQL does not always need to finish the sorting before it can apply the limit. In some cases, you can think of LIMIT as a buffer that MySQL fills up as it sorts the results - MySQL stops as soon as the buffer is filled. For example, if you use LIMIT 10 with ORDER BY, MySQL ends the sorting as soon as it has found the first 10 rows of the sorted result, rather than sorting the entire result. Here's some caveats
Some other random musings
- Interesting debunking of the myth that MySQL violates the SQL standard when it comes to specifying (or omitting) columns referenced in the SELECT list of a query to also appear in the GROUP BY clause
- Another post shows how GROUP BY can get you into a whole world of trouble on this score
And as if that wasn't enough, there'll be a few more updates to this article at a later date. Coffee permitting!
Ruby Bindings for Qt: Building QtRuby on MeeGo and creating an RPM
MeeGo is great. What could make it even better? More Ruby
So what better way to achieve this than by getting the Ruby bindings for Qt up and running on MeeGo. So here's a Work In Progress guide...
Pre-requisites
Get the MeeGo SDK running on your machine under Xephyr http://wiki.meego.com/MeeGo_SDK_with_Xephyr
I'll assume that you can successfully run the meego-sdk-chroot command and are now at a MeeGo console. Here's what we need to do to build QtRuby...
The Guide
Install cmake:
zypper in cmake
Pull down the kdebindings source code from svn:
svn checkout -N svn://anonsvn.kde.org/home/kde/trunk
cd trunk
svn up -N KDE
cd KDE
svn up kdelibs kdebindings
# The above should be all you need to do but if you have problems then maybe try reading http://en.opensuse.org/openSUSE:KDE_developers_guide
Copy the kdebindings/qtruby directory to your meego home directory in the <meego_sdk_directory>, the latter will be something like /home/yourusername/meego-sdk-0524/home/meego/ depending on where you installed the MeeGo SDK earlier.
Copy the file kdebindings/CMakeLists.txt.qtruby to
cd <meego_sdk_directory>/home/meego/qtruby
Edit the new CMakeLists.txt, underneath the line "include (MacroOptionalFindPackage)" add the lines:
include (MacroOptionalAddBindings)
include (MacroLogFeature)
include (CheckCXXSourceCompiles)
Also, just before the line "add_subdirectory(smoke)" add the ine:
add_subdirectory(generator)
Finally, if you want to be able to build an RPM (or any other package type supported by the CPack tool), add the following line as the very last line in the CMakeLists.txt file:
include(CPack)
Ok now we need to create a cmake/modules directory
mkdir -p qtruby/cmake/modules
cd qtruby
As the CMakeLists.txt file says in the commented section, we need to copy some files we need from the KDE related directories (this is because we are building QtRuby standalone instead of as part of the all-encompasing kdebindings module). Note, you need to change the location of kdelibs in the next command to wherever you pulled down the subversion directory
cp kdelibs/cmake/modules/FindQt4.cmake ./cmake/modules/
cp kdelibs/cmake/modules/FindRUBY.cmake ./cmake/modules/
cp kdelibs/cmake/modules/MacroOptionalFindPackage.cmake ./cmake/modules/
cp kdelibs/cmake/modules/MacroPushRequiredVars.cmake ./cmake/modules/
Additionally you will need to copy the following files
cp kdelibs/cmake/modules/Qt4Macros.cmake ./cmake/modules/
cp kdelibs/cmake/modules/Qt4ConfigDependentSettings.cmake ./cmake/modules/
cp kdelibs/cmake/modules/HandleImportedTargetsInCMakeRequiredLibraries.cmake ./cmake/modules/
cp kdelibs/cmake/modules/MacroLogFeature.cmake ./cmake/modules/
cp kdelibs/cmake/modules/CheckCXXSourceCompiles.cmake ./cmake/modules/
And also copy these from kdebindings
cp kdebindings/cmake/modules/MacroOptionalAddBindings.cmake ./cmake/modules/
cp -r kdebindings/ruby .
cp -r kdebindings/smoke .
cp -r kdebindings/generator .
Now we are going to create a qtruby_build directory so that we can do an out-of-source build of qtruby. If you don't know what an out-of-source build is, don't worry!
cd ..
mkdir qtruby_build
cd qtruby_build
Create a file called cmake_qtruby. Copy and paste the long commented cmake command in CMakeLists.txt to this file (you can exclude the make and make install commands)
At the end of the last line of this command add a backslash (so that you can continue the command on the next line) and then add the line:
../qtruby
Then change the cmake command options
-DRUBY_EXECUTABLE=/usr/bin/ruby
-DRUBY_INCLUDE_PATH=/usr/lib/ruby/1.8/i386-linux/
At the end of this step the file should look something like
cmake \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DRUBY_EXECUTABLE=/usr/bin/ruby \
-DRUBY_INCLUDE_PATH=/usr/lib/ruby/1.8/i386-linux/ \
-Wno-dev \
-DENABLE_SMOKE=on \
-DENABLE_QTRUBY=on \
-DENABLE_QTWEBKIT_SMOKE=off \
-DENABLE_QTSCRIPT_SMOKE=off \
-DENABLE_QTUITOOLS_SMOKE=off \
-DENABLE_QTTEST_SMOKE=off \
-DENABLE_PHONON_SMOKE=off \
-DENABLE_QSCI_SMOKE=off \
-DENABLE_QWT_SMOKE=off \
-DENABLE_KDE_SMOKE=off \
-DENABLE_KDEVPLATFORM_SMOKE=off \
-DENABLE_KHTML_SMOKE=off \
-DENABLE_KTEXTEDITOR_SMOKE=off \
-DENABLE_SOLID_SMOKE=off \
-DENABLE_PLASMA_SMOKE=off \
-DENABLE_QTWEBKIT_RUBY=off \
-DENABLE_QTUITOOLS_RUBY=off \
-DENABLE_QTSCRIPT=off \
-DENABLE_QTTEST=off \
-DENABLE_PHONON_RUBY=off \
-DENABLE_QSCINTILLA_RUBY=off \
-DENABLE_QWT_RUBY=off \
-DENABLE_SOPRANO_RUBY=off \
-DENABLE_KDEVPLATFORM_RUBY=off \
-DENABLE_KORUNDUM_RUBY=off \
-DENABLE_KHTML_RUBY=off \
-DENABLE_KTEXTEDITOR_RUBY=off \
-DENABLE_SOLID_RUBY=off \
-DENABLE_KROSSRUBY=off \
-DENABLE_PLASMA_RUBY=off \
-DENABLE_QIMAGEBLITZ_SMOKE=off \
../qtruby
Make this command executable and run it
chmod u+x cmake_qtruby
./cmake_qtruby
This should go off prepare your qtruby_build directory for compiling. Once it is finished, you should be able to install by any one of the following methods
RPM-based Install
You will need to have added the include(CPack) to the CMakeLists.txt file as outlined earlier before running cmake. You also now need to install the following:
zypper install rpm-build
zypper install meego-rpm-config # Maybe you dont need this but I installed it anyway
Now all you need to do is run 'make package' and then 'cpack -G RPM' to build the RPM. You will find that the generated RPM install is called kdebindings-0.1.1-Linux.rpm. That's because we were originally pulled all our qtruby code out of the kdebindings codebase - you can customize the package name, version and much much more by passing parameters to the cpack command when generating the rpm. You can check what values were used to build the package by viewing the CPackSourceConfig.cmake file. As I was just building this rpm for personal use, I didn't bother changing any package details. Tut! Tut! Anyway, once you have an rpm you are happy with, simply install the QtRuby bindings on your system via:
rpm -i <your_generated_rpm_name>
Note: If you want to see the RPM spec file generated, have a look at _CPack_Packages/Linux/RPM/SPECS/kdebindings.spec
Deb-based Install (and other package types)
This is pretty much identical to the RPM-based Install. The difference is to use 'cpack -G DEB' instead of 'cpack -G RPM'
Source-based Install
All need to do 'make' and 'make install' to get the QtRuby bindings installed onto your system. Not quite as easy to undo as an package based installation though!
Testing that everything works
I only tested the RPM package installation method. I found that when running the rbqtapi command I got the following error
/usr/lib/ruby/site_ruby/1.8/i386-linux/qtruby4.so: libsmokeqtcore.so.3: cannot open shared object file: No such file or directory - /usr/lib/ruby/site_ruby/1.8/i386-linux/qtruby4.so (LoadError)
from /usr/lib/ruby/site_ruby/1.8/Qt4.rb:5
from /usr/local/bin/rbqtapi:6:in `require'
from /usr/local/bin/rbqtapi:6
This was because some of the qtruby libraries installed into /usr/local/lib which my MeeGo system does not check for libraries by default. I changed this by adding the file /etc/ld.so.conf.d/qtruby-i386.conf which simply contained the line:
/usr/local/lib
And then running the ldconfig command. Then the rbqtapi command worked happily!
This guide still needs some polish. We'll get to that eventually, but hopefully it may be of some use in getting the Ruby and Qt playing real nice together on MeeGo!
Enabling mod rewrite on Apache
To see if mod rewrite is enabled just create a simple php page with the following content
<?php
echo phpinfo();
?>
When you browse to this page you should see mod_rewrite listed as a loaded module.
If not, then you gotta enable mod_rewrite for Apache
sudo a2enmod rewrite
sudo /etc/init.d/apache2 restart
Now you can create a .htaccess file into the root of your site and start adding rewrite rules. Here's a simple example to make sure rewrites are working
RewriteEngine On
RewriteRule (.*) http://www.google.com/
When you browse to your site, you should now be redirected to Google - if not, don't fret! There's one other gotcha you should know about! Look in the configuration file for your site under Apache (eg. /etc/apache2/sites-available/default) and you will see a <directory> element for your site (careful, there could be a few different <directory> elements floating around, be sure to pick the right one). This will have an AllowOverride attribute which is likely set to None. This is the problem - if you want rewrite rules to take effect then AllowOverride should not be set to None. Try changing it to All. And hey presto, you should be good to go!
This thread on Ubuntu's forums proved invaluable in explaining this.
Awesome Linux Media Center using XBMC
One of things I have been doing lately is rediscovering my music - inspired by talking to the some of the Amarok guys at Akademy this year! So I decided to go on a search for an Open Source Media Center solution and up popped XBMC. Now this project isn't related to Amarok but that doesn't stop it being way too cool for school! I've being tinkering with it for just a few days and it just works like a dream.
Installation on Kubuntu is simple
- I just following the Install XBMC on Ubuntu HOWTO
- In (KDE's) System Settings -> Autostart, added XBMC Media Center to startup automatically on computer startup
- From within XBMC itself, went to System -> Settings -> System -> Video Output and set the Resolution to be Windowed (as I usually want to be able to access my KDE desktop at any time)
- Initially you will have no music library. Instead you will add a directory location containing your music via Add Source. Then you can browse the added location and choose to Scan Item to Library to build up your music library
- By visiting System -> Settings -> Network -> Services you can Allow control of XBMC via HTTP by setting Port, Username and Password. If you know the IP address of the machine you are running XBMC on then you can simply browse to http://YOUR_MACHINE_IP_ADDRESS:PORT from any web browser enabled device to control your media center (for example http://192.168.1.7:6666). Note: XBMC must be running on the machine for this to work
- Once you have your music library set up, you will notice that you cannot access it through the web interface. To enable this, simply edit
~/.xbmc/userdata/sources.xmlby adding the following to the <music> element<source>
<name>Library</name>
<path>musicdb://</path>
</source>
Of course, when I don't need to a complete Media Center solution and just want to rediscover my music I fire up my beloved Amarok
Simply adding a Linux Partition via fstab
Just add something like the following to /etc/fstab...
UUID=1208c5dc-d4a6-a7a2-8352-ab12ecd64412 /media/disk-1 ext4 relatime,errors=remount-ro 0 2
... where
- UUID uniquely identifies the partition you wish to add (you can get the UUID for you drive using
ls -l /dev/disk/by-uuid) - /media/disk-1/ can be changed to be location on your system - it's where the partition will be added
- I don't really have a monkeys of what the other settings do, but I think they are more or less the usual defaults. Works for me!
Bang! Short blog post! Goodnight!
Hello Planet KDE! Ruby makes an appearance in Grantlee
So this will be the first time theirishpenguin makes it onto Planet KDE! And no better time - blogging straight from the KDE community feast that is Akademy! It's been a superb week, in the stunning city of Tampere in Finland. It's Day 6 of the event, a day which has been quite a Ruby-tinted one. First up, I had the pleasure of hacking on Grantlee, a Django-inspired string templating engine in Qt, with Stephen Kelly; adding Ruby support to the code generator example it ships with. Also, after talking to Cornelius Schumacher from OpenSUSE I learned that Ruby's splashed all over the place - even helping power the OpenSUSE Build Service which allows packages to be easily built for any distro. Cool, eh?
Grantlee's an interesting project already in use in Akonadi integration and KJots, providing an elegant templating solution. It's available on gitorious.org in the Grantlee repo. It was good fun hacking on it, particularly useful picking up on some of Stephen's Ninja skills with git! At least it gives a couple of Irish lads something to do while all the Germans and Spaniards are talking about the World Cup!
The organisation of Akademy 2010 has been top notch, from the welcome packs with all the details you need to get oriented - to the big screen for the footie in the hacker room. This was matched by the friendliness of everyone who turned up to the event and the local Finnish. Even these two fellows had a great time coding...

I feel a duck typing joke coming on. Me too!
There's been some interesting BoF (Birds of a Feather) sessions, in particular the KDE Bioinformatics session with Luca Beltrame and KDE for Scientists session, again with Luca and also Stuart Jarvis. Some of the ideas raised pushed me to start working on getting ActiveResource support into Qt on Rails, to make hitting remote APIs possible from a Qt client app.
Well it's 15 minutes to kick off in tonight's semi-final. If anyone out there wants to talk about anything Ruby, or get a quick demo of Qt on Rails, then feel free to ping me. You can comment to this post or find me on twitter (theirishpenguin).
Last night we went Dutch... Tonight who knows...
Developing a simple Match Schedule N900 App for the group stage of World Cup 2010 via Qt on Rails
Today we're going to take a quick look at how to create a N900 app by taking a simple existing Ruby on Rails application and turning it into a Maemo app using Qt on Rails. The main thrust of this blog post is to show how you would tweak the skeleton app generated by the Qt on Rails framework into something that might be useful in the real world. The Match Schedule app is very basic and only shows the upcoming fixtures for the day. But most iPhone apps are simple thin wrappers around a data layer anyway; and this is really only a proof of concept app, so I'd don't feel to guilty about my humble achievement.
When the World Cup kicked off, I really wanted to have a schedule app on my N900 and couldn't find one so hence the motivation. Bear in mind that in 2 days this app will be completely useless as the group stage will be over! Warning: it currently requires a level of technical ability to install this app on N900 as it has no installer. You should check out this related blog post on deploying your Qt on Rails apps on the n900 (Maemo) before tackling this one.
The application (source code) is available for download. Note: I haven't stripped out unnecessary skeleton code from the application, which would exist immediately after generating the Qt application off the Rails codebase. The unnecessary code is related to Create/Edit/Delete functionality which we won't need in our simple Match Schedule viewer. I left it in to show the minimum amount of work needed to tweak the generated app into a useful real world program. All in all (blog posts and stuff aside), it took about an hour to do. If I had to do it again I'd imagine it would take less than half that time.
All the following steps are done on your dev machine. At the end of the guide you'll see how to deploy to your N900.
First we create a Rails app.
rails WorldCup
cd WorldCup
./script/generate scaffold Fixture when:string group:string match:string
rake db:migrate
Then I fired up the web server ./script/server and manually entered the fixtures (stupido! I know!). The 'When' field has the date formatted as '24/6 - 15:00'.
Next up, we turn the Rails app into a Qt app using Qt on Rails. We are still in the WorldCup directory.
./script/plugin install git://github.com/theirishpenguin/qtonrails.git
./script/generate qtify Fixture
This generates the skeleton Qt app. Now let's bend it into shape, starting with the UI. From now on we'll be working in the qtonrails/ plugin directory.
cd vendor/plugins/qtonrails
designer-qt4 app/qdesigns/qmainwindow.ui
Once Qt Designer appears, remove the File menu, Commandlink navigation buttons and Action buttons (by more or less right-clicking on those widgets and deleting)
Then regenerate a Ruby code version of the ui files (every time you change the .ui file using Qt Designer you need to do this)
rbuic4 app/qdesigns/qmainwindow.ui -x -o app/ui_proxies/qmainwindow.ui.rb
./run # or it that doesn't work try: ruby run
Then I got errors
. Based on these errors, I changed the following..
From app/qpresenters/main_window_presenter.rb I deleted
connect(@ui.viewButton, SIGNAL('clicked()'), self, SLOT('view_clicked()'))
connect(@ui.newButton, SIGNAL('clicked()'), self, SLOT('new_clicked()'))
connect(@ui.editButton, SIGNAL('clicked()'), self, SLOT('edit_clicked()'))
connect(@ui.deleteButton, SIGNAL('clicked()'), self, SLOT('delete_clicked()'))
connect(@ui.fixturesNavLinkButton, SIGNAL('clicked()'), self, SLOT('fixtures_nav_clicked()'))
connect(@ui.actionQuit, SIGNAL('triggered()'), self, SLOT('close()'))
Now let's try again.
./run
Hey it worked! Cool! There's more stuff we could now delete but we won't as we're focusing on doing the bare minimum.
In order to allow a column to be correctly resized and to provide row select behaviour (as opposed to having individual clickable cells), I added the following line just before the end of the initialize() method in app/qpresenters/main_window_presenter.rb
@tableview.resizeColumnsToContents()
@tableview.setSelectionBehavior(Qt::AbstractItemView::SelectRows)
The resizing of columns to fit their contents will probably become the default in a future Qt on Rails release.
Due to silly bug in Qt on Rails that tries to pull an unnecessary KDE library into generated applications (Issue 2 on the GitHub Tracker), we need to remove the line require 'korundum4' from vendor/plugins/qtonrails/app/ui_proxies/qmainwindow.ui.rb and vendor/plugins/qtonrails/app/ui_proxies/fixture_qform.ui.rb
In order to display just today's fixtures, we can change the index action in app/qcontrollers/fixtures_controller.rb (again under the qtonrails/ plugin directory)
def index
accept_current_fixtures_from Fixture.all
end
... and add the private method
def accept_current_fixtures_from(fixtures)
fixtures.reject do |fixture|
dt = fixture.when.split(' - ')[0] # Get date from string
date_args = (dt.split('/') + ["2010"]).reverse.map &:to_i
Date.new(*date_args) < Date.today
end
end
Note: The application source code available has the accept_current_fixtures_from() call commented out. This is because once the World Cup group stage is over in a couple of days the list of fixtures would be empty. I have decided that the value of this app as a useful demo in future outweighs the needs of my users over the next two days
. In the source code you can simply add the call back in yourself if you wish.
Finally, we make the grid readonly. Because it was late when I did this, I skipped any fancy meta-programming and simply reopened the QtrTableModel to do so. Add this to config/environment.rb
class QtrTableModel
def flags(index)
return Qt::ItemIsSelectable | super(index)
end
end
Phew! Done! To deploy the app to your N900, read the instructions at deploying your Qt on Rails apps on the n900 (Maemo).
Well, hopefully you've gotten a flavour of how to use Qt on Rails in a simple real world N900 app. If you've any feedback then please get in touch! Until the next time, enjoy the World Cup and I hope your country does well!
Deploying your Qt on Rails apps on the N900 (Maemo)
Qt on Rails is a framework to let you turn your Rails sites in desktop applications and harness the power of Ruby! It's not at production level yet but it's certainly possible to have a good play with it and a bit of a hack! If you're not familiar with Qt on Rails then a good place to start is this blog post covering the v0.1 release. Also, check out the github repo for more info on installing Qt on Rails on your desktop and building an application with it. Here we show you how to deploy Qt on Rails based apps on your N900. One of the goals of Qt on Rails is to provide an easy way for you to develop apps faster for Maemo and, down the road, hopefully MeeGo too!
Note: This blog post may help you figure out how to install any QtRuby application on the N900, not just Qt on Rails apps. Also, this QtRuby Maemo wiki article was particularly useful when I was stumbling along this path!
One thing you will need to install as part of this guide is Easy Debian. Easy Debian greatly expands what you can do with your Maemo device. It basically sticks a full-featured version of Debian on your device. This means 2 things - firstly, for the uber-geeks out there it let's you fire up a Linux desktop on the N900; though it's important to note that your normal Maemo desktop isn't affected by Easy Debian. Secondly, having a full-on Debian available let's you run Linux apps such as Open Office! Sweet! And what rocks is that you can even run these programs without invoking the Easy Debian Linux desktop - in a seamless manner. It's important to note that the user interface to these Easy Debian-based apps behave a differently to a typical native Maemo program; rather they work like a traditional desktop application with a mouse pointer on screen.
- Note: For simplicity, this guide assumes you are installing an application which stores data using sqlite3. Also, the steps here have been tested against the N900 firmware update PR1.2. If you are using an older version of the firmware you may want to consider updating it.
- Firstly, install Easy Debian with the N900's Application Manager
- Install the Easy Debian image via the new Deb Img Install application added to your list of applications
- Note: This is a 1 gig download, but comes with cool stuff like OpenOffice and intergrates pretty seamlessly with your desktop
- Takes an hour or so to download and then extract itself
- Open the Debian Chroot terminal (not the usual N900 terminal), which should now be in your list of applications
- Install rubygems, qtruby and sqlite3 with ruby bindings
- sudo apt-get install rubygems
- sudo apt-get install libqt4-ruby
- apt-get install libsqlite3-ruby
- Install the bits we need need from Rails (without installing documentation)
- sudo gem install activerecord activesupport activeresource --no-ri --no-rdoc
- Zip up your Qt on Rails application and copy to any directory on to the N900. Note that the Qt on Rails application consists of the entire Rails directory directory including the vendor/plugins/qtonrails directory intact and a sqlite3 database already created under the db directory).
If you don't have your own Qt on Rails application to hand then you can create the RadRadio app discussed in the "Make it so, Jim!" section of the v0.1 release blog post
In the Qt on Rails v0.1 release there is a bug that accidentally introduces a dependency on a korundum library, which is not needed in this case. An issue is logged against this in the Qt on Rails Issue Tracker As a workaround, find and remove any occurrences of
require 'korundum4'in files under thevendor/plugins/qtonrails/appdirectory - Once transferred, simply unzip it on your device. Note: If you saved the zip to the Documents folder on your N900, this can be found under /home/user/MyDocs/.documents when poking around the filesystem
- Finally, via the good ol' Debian Chroot terminal, change directory to the vendor/plugins/qtonrails directory of your app and execute
ruby1.8 run - Boom! You should see your Qt on Rails app in all it's glory!
Note there is a bug where you cannot input into a text field when running a Qt on Rails app on the N900 using above technique (seamless mode). As a workaround, open the Qt on Rails app inside of the Debian LXDE desktop (rather than in seamless mode). You can find Debian LXDE in the list of applications on your device. Inside Debian LXDE, open a terminal and run the application as above. Just a quick heads up, sticky keys don't work like you'd expect - you have to hold down the Shift and Fn keys to use them.