Tigraine

Daniel Hoelbling-Inzko talks about programming

Execute callback once multiple ajax requests finish with jQuery

I just stumbled upon the problem of having to load multiple resources during page initialization. During the initial load I wanted a way to prevent any animations and other fancy stuff from happening but rather only create the page as soon as possible.

Since I couldn't find any way in jQuery (there are no combined callbacks) I decided to implement this using the deferred promise concept jQuery 1.5 introduced.

Deferred basically means "I represent a ajax call that has happened or is happening - ask me about it and I'll tell you when it's done or was done already". So implementing my little wait functions was rather trivial:

I used CoffeeScript for this, but the resulting JavaScript is also quite readable and you can use that too.

What this does is provide you with the awkwardly named function waitForAjaxRequestsToComplete that takes one callback parameter and an array of jQuery promises. Once all promises succeed or fail the callback will be called and the first parameter will contain information about failures.

Sample usage:

waitForAjaxRequestsToComplete(function (info) {
  console.log("requests failed " + info.failures)
  console.log("requests succeeded " + info.successes)
  console.log(info.failed) //contains all failed promises
}, [ $.ajax(...), $.ajax(...), $.ajax(...) ]);

Securely managing database.yml when deploying with Capistrano

The more I venture into Ruby land the more magic Unicorns I find on the way. The wonders of SSH still seem totally outlandish to someone used to do deployments by RDPing into a server and xcopying a directory structure into your IIS folder.

But here I am and learning the ways of Capistrano and how deployments to multiple servers really should work.

Naturally I ran into issues I'll detail a bit later, but one of my major problems with my Rails deployment was the different database.yml between my production and my dev environment. Since the repository is in a shared location I could not put the production server mysql password into the config as it would be available to anyone with read access to the repository. This may be something you can get away with in a corporate environment, but if you plan on ever open-sourcing your project you should make sure you don't put production passwords into your repository :).

My solution to that problem is quite simple: I ssh'd into my server and put a "production" database.yml into the home directory of my deployment user and added the following task to my Capfile:

namespace :db do
  task :db_config, :except => { :no_release => true }, :role => :app do
    run "cp -f ~/database.yml #{release_path}/config/database.yml"
  end
end

after "deploy:finalize_update", "db:db_config"

The after statement tells Capistrano to run the db_config task right before finishing the code update, but before running any migrations in case you run cap deploy:migrations (capistrano process). And during every deployment I overwrite the database.yml from the repo with the one on the server.

I also added a assets:precompile task since Capistrano won't run the precompilation of Rails assets out of the box (you need RVM integration for this though):

  task :precompile, :role => :app do
    run "cd #{release_path}/ && rake assets:precompile"
  end
after "deploy:finalize_update", "deploy:precompile"

Et voilá: I can now run cap deploy:migrations from my dev machine and it will automatically connect to my release server, pull the code out of the git repository, compile the assets and migrate the database to a new version. And it will even roll-back to the old version if something goes wrong along the way.

Ps: I also struggled at one point with the SSH keys for the git repository. Since the deployment user on the server has no own private key I was inclined to generate one and add it to my git server's allowed keys list. But that's apparently the wrong way to go about things. The right thing to do here is to simply enable agent forwarding so the server will forward any questions about keys to your dev machine that should have the appropriate set of keys available.

ssh_options[:forward_agent] = true

Filed under rails, ruby, tools

The right thing to do: Rails deployment on Passenger

Today I deployed my very first Ruby on Rails application to a production server and failed. And I didn't fail so much because Rails is hard to deploy (the opposite is true) - but rather because Rails prevented me from doing something stupid!

But I digress, let's start at the beginning: I already wrote about my server setup last week and today was the time to deploy a Rails3 application to my Apache/Passenger.

The Passenger documentation really made things easy. I just cloned my repository to a folder and setup everything accordingly. Then I did the obvious chown -R www-data:www-data . so the www-data user Apache uses has full access to the whole application directory and fired up my browser.

And it didn't work. Passenger was obviously running as I was able to load files from the /public folder without a problem, but the routes inside my application only returned errorcode 500.

Why? Simple: Passenger by default assumes you are running in a production environment so it will load that up from your database.yml. So I had to make sure that those settings where correct (easy one). Next problem on the list was that any rake commands you are running on the server still assume you are using the development environment. So in order to create your databases and migrate the schema you have to do a:

  rake db:create RAILS_ENV="production"
  rake db:migrate RAILS_ENV="production"

Otherwise db:create and db:migrate will always run against the development environment settings inside database.yml.

But once that problem was solved, I was still getting 500s.. So I opened the /log/production.log and found another gem I really liked: Rails refused to start at all because I was running in production without having precompiled the assets (javascript/css)!

A simple rake assets:precompile fixed that, but boy was I impressed. I knew about the different environments in Rails and saw how they would affect deployment scenarios. But having Rails just throw exceptions at me if I try to do something so obviously stupid as to not have JavaScript and CSS minified and merged together into one file is really awesome. Especially when coming from a .NET environment where up to this date the <= .. => syntax does not safely URL-Encode the value (and it took them 10 years to release the safe <: ..> alternative, having a framework that throws you into the pit of success is exhilarating.

Filed under programmierung, rails, ruby

Site migrated - more awesome - more root!

The title actually says it all. I just finished the migration of my blog to a new server I am now administering myself (wether that's a good thing or not still remains to be seen).

Last week I noticed that I could get a full root server for the same money I am paying for the 100 megs of PHP4/MySQL4 hosting at my old provider. So I jumped at the opportunity and got a nice little Debian Squeeze VM from my good friends at CoUnity.at.

I've now spent two days configuring the heck out of that machine and I must say I am impressed, so here are some notes that may help when you are migrating/setting up.

Git

It's ridiculous how easy it is to install gitolite. You just follow this little guide here and you are up and running your own private Git server in 5 minutes tops.

I actually just canceled my paid GitHub account since I can now host my private Git repositories on my own server now.

Apache / MySql

It's almost kind of silly and feels totally outlandish to anyone who has ever gone through a IIS and SQL Server 2008 install, but getting the full stack running on Debian was once again a two liner on the shell:

apt-get install mysql5
apt-get install apache2

Ruby

One of the main reasons why I wanted my own root server was so I could run my own little rails projects I had nowhere to put before. So naturally I checked for the apt packages on ruby and found out that they are horribly outdated. Since I primarily wanted Rails3 I installed Ruby 1.9.2 through RVM.

Once again, so easy that any Windows Sysadmin will weep:

bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
#restart the prompt
rvm notes
#Read through the notes as it will tell you what packages ruby 1.9.2 requires
#and install them through apt-get
rvm install 1.9.2
rvm use 1.9.2 --default

The rvm install will take about 10 minutes since it's actually compiling ruby 1.9.2 from source and installing it to /usr/local/rvm but that's it then.

Passenger

Being able to run Ruby is nice, but Apache2 didn't know anything of it's luck yet so next step was to install the Passenger in order to run Rack apps.

Now here it's a bit tricky because Passenger is available in the apt sources. So if you run apt-get install libapache2-mod-passenger you are screwed as apt will go ahead and pull down Ruby 1.8 and overwrite all the stuff you did with RVM before. (No big deal but not ideal either).

So the better way to install Passenger is to actually install it through the new RVM rubie as a Gem:

gem install passenger
passenger-install-apache2-module

Again, it can't really get any easier. The installer will even tell you what libraries you are missing and tell you exactly what to type into apt-get to install them.

After the install is done you simply need to hack open your apache2.conf and add the module settings (they are listed in the installer), but it's better to go into your /etc/apache2/mods-available and put the code into a new file (conveniently called passenger-local.load). Now your local passenger install is an apache module and apache won't blow up on the next apt-get upgrade.

You then activate your module with a2enmod passenger-local and restart apache (/etc/init.d/apache2 restart).

Wordpress

Migrating a Wordpress blog was fairly easy, I just copied all the files from my old host into /var/www and restored the MySQL database from the mysql-dump. The thing that amazed me at this point however was Sequel Pro and SSH. The MySQL server does not allow connections from the outside for obvious security reasons so I was already thinking about either installing phpMyAdmin on the machine or figuring out how to run the 5 mb SQL insert script through the MySQL commandline client. Turns out, my Mac MySQL app Sequel Pro can connect to MySQL servers over SSH. So I connected to 127.0.0.1 over the encrypted SSH tunnel - getting a really nice management interface with no open ports whatsoever. Wow..

Anyways, the Wordpress install was pretty easy. I set the correct file permissions on my /var/www folder with chown -R www-data:www-data /var/www and changed the database connection strings in wp-config.php. I then had to fiddle a bit because mod-rewrite was not enabled and all permalinks where broken. I fixed that with a2enmod rewrite and setting the AllowOverride setting in /etc/apache2/sites-available/default to FileInfo

In closing I have to say I am genuinely impressed by the amount of flexibility you get with SSH. Windows could really use a good command line interface and desperatly needs a way to remote into servers without having to run RDP all the time. Especially sending files around with SCP or tunneling traffic over SSH is so incredibly useful that I can't understand why there is no such thing in Windows at all. Your best bet there is PowerShell remoting but that's far away from useful.

Filed under site-news

SSH address book

This may be old news to anyone somewhat used to Linux server administration, but Jammm just enlightened me so I thought I'd share.

Say you don't have a hostname associated with your server (yet), you may get bored of writing ssh [email protected] all the time. Assuming you are using public key authentication anyways (and you should!) writing ssh myserver would be far more convenient.

Turns out you can do that by modifying the ~/.ssh/config file like this:

Host myserver
  HostName 192.168.0.1
  Port 22
  User root

Filed under tools

My Photography business

Projects

dynamic css for .NET

Archives

more