Where Ruby leads, others follow…
In response to the question and comments on a recent linked in post discussing the suitability of various web platforms, including Ruby on Rails...
The first big decision
Firstly, whatever you choose, I would suggest using a platform that is Open Source. This means that the stack is effectively commoditised - giving access to a high quality platform that keeps your costs low today and into the future as you scale out and avoid being locked into the release cycle of one particular vendor. A big plus is that anytime you hit a problem, your engineers can drill right down into platform to diagnose and resolve it. This is a formula that's worked for for Google, Groupon, Amazon, Twitter and Facebook to mention a few.
Enter Ruby
I am of the opinion that Ruby and Rails is the likely the best web framework to use to build the vast majority of web applications out there. Since it's release, Rails has lead the way in terms of web development - promoting the Model-View-Controller pattern in a simple, easy-to-use manner as a way of creating web applications. Whilst that pattern has been around for a while Rails really put it on the radar for web apps. Along with it came a shift from unpopular SOAP webservices to a RESTful service architecture. And so too did easy to use AJAX techniques for building richer UI's because Rails bundled the Prototype library with it from an early stage. Rails has since continued to lead by incorporating the latest Javascript technologies into its ecosystem - such as node.js.
Dynamic Language = Dynamic Business
Because of the dynamic nature of the language, its strong use of conventions and the style of Ruby developers, you end up having much less code in your application. This win in terms of a smaller codebase is also true of projects developed using other dynamic languages such as Python. A smaller codebase has the usual touted benefits in terms of rapid development times and a more easily managed codebase. In particular, less boilerplate code.
Using dynamic over static typing means that you have to have to write automated tests to cover your code. Some developers disagree with dynamic typing and say that the compile time checking of a static language is important. In my experience, this is not so. By writing automated tests to cover business cases I've found that I catch a much more important class over of errors - business logic issues - while also covering the code paths for the edge cases that the static compiler would have checked. One of the ways static languages try to provide some of the flexibility of dynamic languages is through generics. This quickly leads to a type of complexity which I have found is much better, and more clearly handled, using a dynamic language. The clarity of the code, the ease of testing and the expressiveness and power of a language like Ruby has firmed up my thinking on this area. As a platform which has baked testing right into the core of its ethos, Ruby and Rails has lead the way in terms of integrating automated test frameworks into the developer's workflow. And a higher level, it has made it possible to bring business stakeholders and development teams together by introducing tools that allow one to express business features in terms that can easily be discussed with, implemented and tested by a developer. Cucumber is probably the best example of this, which like many other Ruby projects has caused a massive stir in the web community.
And the bad news...
So what are the pitfalls. I'd agree that it is harder to find Ruby developers. Whilst many top tech locations have a massive amount of Ruby developers - such as San Fran or London - Ireland doesn't have those numbers. This is partially because we have lagged behind the technology curve a little in the past. But I'm happy to say that this is something that has changed immeasurably over the last couple of years. There is a lot more developer meetings and grassroots activity, predominantly in those with an Open Source slant. This is a good news story not just for Ruby, but for the whole Irish tech sector. And I'm sure that strong confident Irish companies will emerge as a result.
Some companies do decide that certain parts of their solution are best written in a different language. There are some well tried paths to doing this with Ruby. For example, you can use JRuby to easily farm out some parts of your codebase to Java while retaining development agility for the rest of your codebase by keeping it in Ruby. However, needing to do this is very much the exception rather than the norm.
Building services and deployment
You indicated that you will be writing a service rather than a full fledged web application - another option you could consider is using the Ruby-based Sinatra as your web framework instead of Rails - which is a thinner layer, which some people prefer for building services such as github. That said, I would still personally go with Rails as there are more tutorials and guides available - particularly if it is your companies first time working with Ruby.
For deployment, Heroku makes developing and deploying Ruby apps a breeze. It really is an exceptional platform which sits on top of Amazon's AWS. I would recommend taking a serious look at it at http://www.heroku.com before making a decision on which platform to choose.
So, all in all, my long standing impression of Ruby and Rails is that it is web development done right. It has been the leader in pushing forward how developers and businesses develop websites and consigned the sprawling mess that existed before it to history. In the meantime other platforms have tried to copy some of its features, often less elegantly. As the same time Rails has accelerated the rate at which it pushes forward the web - the next release Rails 3.1 is breathtaking in its feature list.
For these reasons I think Ruby is 'the' web development language of the next decade. Whatever you decide, all the best with your venture. There are plenty of excellent platforms out there to choose from so it's a great time to be building the kind of high-scale web applications and services.
Bundler installing gems into the wrong directory – mea culpa
So I spent an hour and a half last night unfairly swearing at the spork gem; and blaming it for everything from world hunger to banking crises! This is because somehow when installing spork I had managed to change the location that bundler installs gems into - strangely enough into a directory called ./spork under my project directory. And I figured the blame landed at the door of the spork gem. Here's what happened just so that you don't get caught out.
Once installed, here's how you start up spork: bundle exec spork
But, at some point, here's what I did by accident: bundle install spork
Fool! Fool! Fool! For those of you that aren't familiar with the 'path' argument to bundler, well, what it does is permanently change your path to the value you specify. So for an hour and a half last night, I had ./spork set as my gem directory. Meaning that my furious efforts to correct the problem were in vain - such as clearing out all the gems and reinstalling, grepping for a config file that could be pointing at ./spork in my project and rvm directories.
What I should have done to fix this problem immediately, was to tell bundler to use my standard rvm directory for gems again
bundle install --system
Ta da!
It's pretty confusing that specifying a path argument once off should cause bundler to change it behaviour on a permanent basis but at least I notice that in the docs it says
The path argument to 'bundle install' is deprecated. It will be removed in version 1.1. Please use 'bundle install --path spork' instead.
So I guess I'm not the only one who's hit this confusing behaviour. Long live the --path option!
Sorry spork, I'm a dork...
Machinist 2 and the undefined method for Sham:Module error
This ain't no Sham marriage! Machinist 2 has done away with the Sham module. But, at time of writing, this isn't immediately obvious from the installation guide. So if you try to horseshoe your Machinist 1 (Sham-based) tests into Machinist 2 then you'll find you get an error like
/home/you/dev/rails/my_project/spec/support/blueprints.rb:5:in `
The solution is to use the new serial_number() method technique instead. For more info on what's in Machinist 2 check out the What's new in Machinist 2 page.
A short post but hopefully it helps someone. Mock on!
Push and pull data between your local MongoDB and Heroku or MongoHQ
Heroku do a great job of providing a free way to host MongoDB. The only slight issue I had was syncing data between my machine and my Heroku apps - in the way I was used to with their Taps plugin which works for Postgres databases. But here's how to do it for MongoDB. Note, I was using it with a Rails 3 and Ruby 1.9.2 app.
Firstly you need to get Pedro Belo's super plugin heroku-mongo-sync . The docs say that the default way to use it is just to do a 'heroku mongo:push' or 'heroku mongo:pull' from inside your app directory on your machine. But this takes the name of your app exactly and assumes that this is the name of your mongo database. Unfortunately, many people like to call their database something else - and even the default is yourappname_development which won't work (it must be 'yourappname' exactly).
A feature allowing you to specify a MONGO_URL shell variable that will let you override this is in the README. Sadly it didn't work for me. But help is at hand - we can just create a new enviroment for syncing and then specify the database we need as the default. Of course you don't have to do this if your database is actually exactly the same as your app already; though the following approach may still serve you well as a workflow
* Pre-requiste, you must have an account setup on MongoHQ for this to work. And see here for how to setup MongoDB on Heroku
* Copy your config/development.rb to config/sync.rb
* Ok, I'm using Mongoid. Here's how I define my db connection to be exactly the same as my app's name in my config/mongoid.yml file
sync:
<<: *defaults
database: yourappname
* Whenver you want to push or pull to Heroku just change the default Rails environment at the top of your config/application.rb file to be 'sync'
require File.expand_path('../boot', __FILE__)
ENV['RAILS_ENV'] = 'sync'
* Now from the command line, you should find 'heroku mongo:push' and 'heroku mongo:pull' work like a dream. Ahoy, me mongols!
Installing Ruby on Rails on MeeGo with SQLite
This post will go through installing Ruby on Rails 2.3.2 on MeeGo - though it should likely work for any version of Rails, including Rails 3 (though the actual Rails commands at the end of this guide will be a little different). Firstly go through the Installing Ruby and Rubygems on MeeGo guide to get at least Ruby 1.8.7 on your system and then carry out the following steps.
PRE-REQUISITES
* The above guide requires you to disable the core repository and then enable the devel and testing repositories. I found that when following the below steps, I would get an error trying to install sqlite3 gem itself - saying that the sqlite3.h header could not be found. I had to run the following commands first
# Update all packages on the system
sudo zypper update
# Then the chrome browser would not work saying
[declan@declan-desktop grr]$ chromium-browser/usr/lib/chromium-browser/chromium-browser: error while loading shared libraries: libicuuc.so.42: cannot open shared object file: No such file or directory
# To fix this, I had to update chromium-browser separately
sudo zypper update chromium
Ok, so onto the actual install steps for Rails and SQLite3...
RAILS INSTALLATION
#Install ruby and sqlite development headers (as we'll be using sqlite as our backend)
sudo zypper install ruby-devel
sudo zypper install sqlite-devel
# Install tools for building C-based gems
sudo zypper install make # Not 100% sure that make is needed
sudo zypper install gcc
# Install sqlite3 gem for ruby
sudo zypper install sqlite3-ruby
# Install Rails
sudo gem install rails -v 2.3.2
# Create a new Rails application
rails grr
# Create a thing (ok, more correctly called a resource) in Rails
cd grr
./script/generate Animal name:string
# Create the database
rake db:migrate
# Fire up the Rails web server
./script/server
# And then browse to your site in the web browser - http://localhost:3000/animals
Hurrah! Everything works! (At least I hope it did!). Happy Rails development!
Supressing hrefs in image hyperlinks when deleting resources using jquery
This week we're trying to delete a resource using jquery within a Rails 2.3 app. First thing to note when using Rails is that if you set :method => :delete like below you will get a whole bunch of obtrusive prototype library javascript injected into the hyperlinks onclick() event. This can clash with your jquery code so you can't use :method => :delete alongside jquery. And it doesn't make any sense to do it this way. The number one reason that someone (err... yes, me!) will get into this mess is that they've been using the image_tag and link_to helper's and just forgotten that all this extra prototype code will be generated. So this is not so good for what we want...
<%= link_to image_tag('/images/delete.png', :title => 'Delete the thing', :class => 'clearPrintQueueOnClick'), '/things/3', :method => :delete %>
But this is better...
<%= link_to image_tag('/images/delete.png', :title => 'Delete the thing', :class => 'deleteThingOnClick'), '/please_enable_javascript.html' %>
Note: That we have jquery code set up against anything that has the class deleteThingOnClick.
Ok, if the user's browser does not have javascript enabled it will take them to the please_enable_javascript.html which you can drop in your public html. If you want to support non-javascript browsers you can create a page with a form for doing delete requests - an example is shown in this blog entry by Andy Gaskell.
With those pitfalls out of the way, all that remains to do to supress the href is to add event.preventDefault(); to the start of your deleteThingOnClick handler
$("a").click(function(event) {
event.preventDefault();
// rest of your deletion code goes here..
}
That's it really! Exit stage left...
How to localize model names in ActiveRecord associations via config locales with Ruby on Rails
Argghhh!!! I went bananas for a little while getting my head around this. What are we trying to achieve? Well, say let's we have a model called Consultant which has a many-to-many relationship with Company though a Contract association. The Contract model is basically sitting on topic of a simple join in the database which has consultant_id and and company_id fields.
class Consultant < ActiveRecord::Base
has_many :contracts
has_many :companies, :through => :contracts
accepts_nested_attributes_for :contracts
end
class Company < ActiveRecord::Base
has_many :contracts
has_many :consultants :through => :contracts
end
class Contract < ActiveRecord::Base
belongs_to :consultant
belongs_to :company
validates_uniqueness_of :consultant_id, :scope => :company_id,
:message => "cannot have a contract with the same company more than once"
end
We have a New Consultant page which allows us to associate existing Companies with a Consultant by adding/removing Contracts. A Consultant cannot have a contract with a Company more than once, hence we need a validate_uniqueness_of validation on the Contract association.
But hey, business is booming! We end up needing to reuse the code base for another Rails project. The new project is in the construction domain but it has been decided that the database schema and domain model should remain unchanged. However we are told that as far as the user is concerned they should never see the term Consultant, rather they should see the term Builder. Enter translations!
Though you're not going to put your shoddy Spanish to the test and you failed French before you left school, translations are a handy way to work around this problem. Simply translate the word Consultant into Builder via the config/locales/en.yml file in your Rails project. At the same time we'll also change
- The validation error header that is present when the user submits an invalid record
- The displayed version of the consultant_name to be Builder Name
- The displayed version of the consultant model name to be Builder when arising from errors on the Contract association. The way to do this is not immediately obvious - you have to translate the foreign key field (consultant_id) to Builder for the association
Here's the complete config/locales/en.yml file. Note: Whitespace and indentation is very important.
en:
activerecord:
errors:
messages:
template:
header:
one: "This Builder has just one error but still you gotta fix it..."
other: "This Builder has lots of errors, get your act together..."
attributes:
consultant:
consultant_name: Builder Name # Handles the work of translating this attribute
contract:
consultant_id: Builder # NNB: This the big one! Notice how must translate the foreign key field to Builder for the association!!!
models:
consultant: Builder # This causes the consultant model to be referred to as Builder on the UI
So there you have it. The foreign key is the key, so to speak!
The best guide I encountered on translation was this one by Ian Hecker on Translating ActiveRecord which is a great way to get started. There is also a somewhat overwhelming guide to translations at Rails Internationalization (I18n) API but is comprehensive nonetheless. Also, do look at the en-GB.yml example at Sven Fuchs Locale Examples on GitHub.com which is where I first saw how you can define you locale file as an .rb file or yaml as above. Finally, I came across this entry on how to remove Rails Validation Message Prefixes which I didn't try but I just mention here in case I need it in future.
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.
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!
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!