Since the redesign of eBox platform, our bounce rate increased dramatically. After a short investigation, it made sense: our new website was just the homepage and news, and the rest of the content was on different domains (trac, eBox Technologies, …).
So our bounces were either real bounces, or people visiting our other sites (which I wouldn’t count as bounces).
My solution: track outgoing links.
I searched for a solution and found this article, but it wasn’t exactly what I wanted.
It’s a good first approach, but tracking external links as pageviews makes the analytics reports more confusing. Events Tracking API to the rescue! This API was conceived to track actions that don’t match a page view, like video plays and other application interactions.
So, with events we could track our exits separately, get the information we need, and get a more accurate Bounce rate.
The extra code:
The original article used rel="external" to mark the links to track. There is an easiest way: searching for absolute URLS in the href attribute. Also, I’m using the action parameter to differentiate between internal (our other websites) and external (facebook, twitter,…) links.
What we are tracking, and will be able to see on the Analytics reports is:
Category: Exits. Could have been called ‘outbound links’, or any other variation
In general, a “bounce” is described as a single-page visit to your site. In Analytics, a bounce is calculated specifically as a session that triggers only a single GIF request, such as when a user comes to a single page on your website and then exits without causing any other request to the Analytics server for that session. However, if you implement Event Tracking for your site, you might notice a change in bounce rate metrics for those pages where Event Tracking is present. This is because Event Tracking, like page tracking is classified as an interaction request.
For example, suppose you have a page with a video player where the bounce rate is historically high, and you have not implemented Event Tracking for the page. If you subsequently set up Event Tracking for the player, you might notice a decrease in the bounce rate for that page, because Analytics will record user interaction with the player and send that interaction to the server as an additional GIF request. Thus, even though the same percentage of visitors to the page might still exit without viewing any other page on your site, their interaction with the video player triggers Event Tracking calls, which disqualifies their visit as a bounce.
In this way, “bounces” for your event-enabled pages means something slightly different: a single-page visit that includes no user interaction on tracked events.
Since I upgraded to Snow Leopard I’ve been missing readline whe using irb. As I discovered in this article, this is due to apple’s ruby linking to libedit instead of libreadline. I didn’t have that problem before the upgrade since I had compiled ruby myself.
This time, I was looking for another solution. I could have compiled ruby with readline support, but then probably I’d had to reinstall some gems too. So I present you the quick way to fix your readline
Step 0: Setup temp dir
mkdir -p /tmp/rlruby
cd /tmp/rlruby
sudo -s
Step 1: Install readline
curl -O ftp://ftp.cwru.edu/pub/bash/readline-6.0.tar.gz
tar xvf readline-6.0.tar.gz
cd readline-6.0
./configure && make && make install
cd ..
Step 2: Get ruby source
To keep the complications to a minimum, I downloaded ruby from apple (check 10.6.2 open source, or other releases). The current patchlevel is ruby-75 so fetch that one:
curl -O http://www.opensource.apple.com/tarballs/ruby/ruby-75.tar.gz
tar xvf ruby-75.tar.gz
cd ruby-75
Step 4: Build readline extension
We don’t need to build all ruby, just the readline extension
cd ruby/ext/readline/
ruby extconf.rb
make
At this point, you’ll probably get the following error:
readline.c: In function ‘username_completion_proc_call’:
readline.c:730: error: ‘username_completion_function’ undeclared (first use in this function)
readline.c:730: error: (Each undeclared identifier is reported only once
readline.c:730: error: for each function it appears in.)
readline.c: In function ‘username_completion_proc_call’:
readline.c:730: error: ‘username_completion_function’ undeclared (first use in this function)
readline.c:730: error: (Each undeclared identifier is reported only once
readline.c:730: error: for each function it appears in.)
lipo: can't open input file: /var/folders/s4/s4qO7oueE3ijABAH7qB6Dk+++TI/-Tmp-//ccW5lOLL.out (No such file or directory)
make: *** [readline.o] Error 1
We need to tell gcc that our readline is in /usr/local
To be sure we are using the real readline run otool and make sure libedit doesn’t appear on the results:
$ otool -L readline.bundle
readline.bundle:
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/libruby.1.dylib (compatibility version 1.8.0, current version 1.8.7)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.0)
Step 5: Replace readline.bundle
cd /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/ruby/1.8/universal-darwin10.0/
mv readline.bundle readline.bundle.libedit
cp /tmp/rlruby/ruby-75/ruby/ext/readline/readline.bundle readline.bundle
Now launch irb and check if all your favorite shortcuts are in place
I sure am. After a proper installation/configuration, the most important factor is to always stay updated to the last version. I’m managing at this time 8 or more blogs/websites running different versions of WordPress and it’s hard to keep them up to date.
Automatic upgrades help, although they still terrify me after the 2.8 crash.
The problem is, some of these blogs are set up for friends or old projects, and I forgot to frequently check if they are using the latest version. Most of the times, they become crammed with spam, and eventually trigger google’s malware detectors. Most of the times I notice the hack because of firefox malware warning.
So I started a side project to help me keep track of all those blogs and their versions, and it’s seems is close to see the light. This is how it looks right now:
I will need testing, so if you want to participate in the beta, fill the signup form, and I’ll send some invitations.
Also, I’m looking for a nice name for the thing. If you have a good idea, put it in the ‘Proposed name’ field on the signup form. The winner(*) will get the first beta invitation and free full access to the product for 1 year after it launches. Make sure a .com domain is available for the name you propose or it won’t have many chances.
I need help with this. I had a dream… Well, not so much as a dream, maybe a “It’d be cool to…”
I thought it’d be nice to discover new photos on flickr using your favorite photos and the people who also favorited those photos, and the favorite photos of those who also favorited my pictures. Still with me?
It’s actually a quite simple code (about 500 lines, check it on github: discovr), but it’s terribly slow. Some possible reasons:
Way too much data. I’ve found people with aroundmore than 18000 favorites, and there are photos with more than 2k fans. After limiting to 50 last favorites, the numbers are still creepy. Following from my personal favorites (366), I discovered 1268 users and 52632 photos
Too complicated for an API. This is the kind of feature that wouldn’t be so hard to implement if you have access to the flickr database directly, but having to do so many requests adds a lot of time to the process.
Inefficient library. I had to do some modifications to the flickr ruby library just to make it work, but it’s still quite inefficient in some cases. Want to know the url of a picture (knowing the picture id)? 4 (completely unnecessary) API calls
My code is bad. OK, I know it’s ugly to start blaming everyone else. I know my code is not very good, as it’s a quick prototype. Still, I’m not sure if making my code/libraries better would be enough improvement given the network/api bottleneck
I’ve done a fair amount of web “design” (mostly implementing designs of others) and development in the past, I usually set up a lot of virtual hosts in my local apache. I’ve done that in three different ways.
The quick&dirty hosts file
Point any of your development domains to 127.0.0.1 in the /etc/hosts file. It’s the easiest way, but you need to add them one by one. At some point, mine could look like this:
127.0.0.1 warp.dev
127.0.0.1 ebox-platform.dev
127.0.0.1 ebox-technologies.dev
127.0.0.1 jorgebernal.dev
127.0.0.1 projectA.dev
# ... and so on
Getting smart with dnsmasq
This is a more automated method. You install dnsmasq and configure 127.0.0.1 as your DNS server. Then add this to your conf:
address=/.dev/127.0.0.1
This worked well, and acted as a dns cache. But I had some trouble with dynamic dns entries at our old office: projects.warp.es would point to a local address inside the office and our remote IP from outside, so I found myself clearing the cache too often.
42foo: the zero-code web service
So I made it external. I bought 42foo.com and set up a bind zone with this:
@ A 127.0.0.1
* A 127.0.0.1
So warp.42foo.com, ebox-platform.42foo.com or whateveryourprojectis.42foo.com always point to 127.0.0.1
You still have to set up the virtual host, but there is one step less for web development. Feel free to use it, and let me know if you set up something similar with a shorter domain name
This week we had our first WarpTalks session. Once a month we’ll gather in our meeting room and someone will deliver a talk, workshop or debate about topics considered interesting.
We opened this Monday with two talks. They are in Spanish but you can get the idea.
Introduction to Subversion by Victor Jimenez
Subversion is the RCS we currently use, and the developers know it well enough to do their everyday job, but the not-so technical people at the company have been expecting some training for a while.
10 things you might not know about MySQL by Jorge Bernal (me)
MySQL is the obvious choice when we need a database for our projects, so many of the developers use it daily. I tried to show some aspects of MySQL which could be useful to them but not the first things you learn about a database.
I’ve been trying to give rebirth to an old internal project at my company, abandoned circa 2006, and this is one of the code pieces I think it could be useful to others.
I wanted to store country information as an ISO code, so here is the plugin to make it work
It’s translated using the translation from the iso-codes package, so I guess it’s as good as it can get by now. If you want to contribute, please do it to the iso-codes package and let me know, so I can update it too.
iso_countries – Store countries using ISO 3166 codes
This rails plugin enables you to store country info only with the country’s ISO-3166 code
Example
class Company < ActiveRecord::Base
iso_country :country
end
c = Company.new :country => "es"
c.country # => "es"
c.country_name # => "Spain"
c.country_name = "France"
c.country # => "fr"
ISO::Countries.set_language "es"
c.country_name # => "Francia"
I’ve been looking for an open source project to collaborate for some time now, and given the time I’m spending with MySQL lately and the expertise I’m gaining thanks to MySQL training, it looked like an obvious choice.
During the last advanced bootcamp, Tobias found bug #27894, which apparently was a simple fix. Dates in binlog were formatted as 736 instead of 070306 (for 2007-03-06). During the bootcamp I used my lonely nights at the hotel and came up with a patch, and some days later my first contribution was going into the main MySQL code.
The problem
Now I had to find something bigger. One of the things that most annoys me of MySQL is the lack of some way to abort a procedure or trigger: there is no raise method. To generate a custom error you have to do hacks like:
SELECT `
Error: Invalid firmware series for this model
` INTO dummy FROM model;
The solution
There is a SIGNAL command in the SQL:2003 standard which does the job, but it’s not implemented (yet) in MySQL. The syntax, according to the manual is as follows:
In this first part I’ll cover the basics: just the SIGNAL command with a fixed generic error, enough to get rid of the dirty hacks.
The implementation
Getting used to foreign code always takes some level of difficulty, but when you have to deal with grammars and parsers it’s all crazy fun. First, we have to add a symbol for our new command
sql/lex.h
In this file, we have a symbols[] array where we have to add SIGNAL. Since it seems to be sorted in alphabetic order, we’ll put our line between SHUTDOWN and SIGNED:
Before we get our hands dirty with the parser file, let’s get our custom error prepared. I took a look at the SQLSTATE error messages and I found the 38503 (Exception generated from user-defined function/procedure) enough related to this.
In this file we have a series of error constants with their corresponding error messages in various languages. Since our new error will be related to stored procedures, I decided to put with the rest of SP-related errors:
ER_SP_CASE_NOT_FOUND 20000
eng "Case not found for CASE statement"
ger "Fall für CASE-Anweisung nicht gefunden"
ER_SP_SIGNAL 38503
eng "Exception generated from user-defined function/procedure"
ER_FPARSER_TOO_BIG_FILE
eng "Configuration file '%-.64s' is too big"
ger "Konfigurationsdatei '%-.64s' ist zu groß"
sql/sql_yacc.yy
And finally to the point. Here we have to declare that we’ll be using the SIGNAL_SYM which we defined at sql/lex.h as a token.
Then, in the sp_proc_stmt label (look for sp_proc_stmt: at the beginning of a line), we add sp_proc_stmt_signal as another possibility (we’ll define this in a minute):
This basically tells the parser to expect the SIGNAL_SYM token (SIGNAL) with no arguments, and generate an error with our new error code (ER_SP_SIGNAL). As you might see there’s some extra code which I copied directly from similar definitions, which I’ll refer to as parser magic (anyone willing to explain what sphead and lex variables are will be very welcome)
Conclusion
This one wasn’t so extremely difficult if you had some previous experience with Bison, but the next part can be more interesting, since I guess we’ll have to add some more functions than sp_instr_error to be able to show custom error messages. Also, we’ll have to prepare some test cases to verify our newly created behaviour.
I hope this helps someone trying to contribute to MySQL. If you want to try this at home you can follow the article or apply the patch
All this began when I discovered jamendo. Shortly, it’s a website where you can listen CC-licensed albums and download them using BitTorrent or another networks. I found quite interesting albums and I decided to try gnome-btdownload for downloading them.
I found this software quite simple and nice, very much in the gnome style, but I couldn’t help to have X windows opened (one for each download/seed), mostly when X → ∞ (or at least a relatively huge number if you try to be nice and share as much as you get).
So I wanted to start hacking on this code and make it download multiple files at once in only one window and hide it in the system tray. I have finished writing my own code albeit I’ve reused all the BitTorrent part.
It’s my first whole desktop program that works and it’s an incredible feeling… I feel like the child who discovered computers years ago