[Tool] SQLite Manager

For a couple of months I've been developing some Adobe AIR applications - all of them require local SQLite databases. To get the job done I needed an application that would ease SQLite DB management.

After some googling SQLite Manager poped up. It's a pretty easy yet powerful tool - can be run both a Firefox extension or a stand-alone application (via XULRunner).

My only wish so far - a decent-looking interface.

SQLite Manager

SQLite Manager

[Hint] Discarding non-ASCII characters in Ruby on Rails

My current Ruby on Rails project requires SEO-friendly URL-s. I've installed acts_as_slugable plugin, but soon I found out that all my slugs contained Polish characters (in UTF-8). After some googling (Polish Ruby on Rails forum) and some more sips of coffee I came up with some modifications to acts_as_slugable.

Here's lib/acts_as_slugable_ascii.rb:

 require 'string'
 
module Multiup
  module Acts
    module Slugable
      module InstanceMethods
        private
          def create_slug
            return if self.errors.length > 0
 
            if self[source_column].nil? or self[source_column].empty?
              return
            end
 
            if self[slug_column].to_s.empty?
              test_string = self[source_column]
 
              proposed_slug = test_string.strip.downcase.gsub(/[\'\"\#\$\,\.\!\?\%\@\(\)]+/, '').to_ascii
              proposed_slug = proposed_slug.gsub(/&/, 'and')
              proposed_slug = proposed_slug.gsub(/[\W^-_]+/, '-')
              proposed_slug = proposed_slug.gsub(/\-{2}/, '-')
 
              suffix = ""
              existing = true
              acts_as_slugable_class.transaction do
                while existing != nil
                  existing = acts_as_slugable_class.find(:first, :conditions => ["#{slug_column} = ? and #{slug_scope_condition}",  proposed_slug + suffix])
                  if existing
                    if suffix.empty?
                      suffix = "-0"
                    else
                      suffix.succ!
                    end
                  end
                end
              end
              self[slug_column] = proposed_slug + suffix
            end
        end
      end
    end
  end
end
 
ActiveRecord::Base.class_eval do
  include Multiup::Acts::Slugable
end

The only difference is the use of to_ascii method.

Now, we need to extend the String class. to_ascii method converts Polish UTF-8 characters to ASCII and discards all unknown characters:

 require 'iconv'
 
class String
  def to_ascii
    ascii = 'acelnoszzACELNOSZZ'
    non_ascii = "\271\346\352\263\361\363\234\277\237"
    to_ascii_string = self
    begin
      result = Iconv.new("CP1250", "UTF-8").iconv(to_ascii_string)
    rescue Iconv::IllegalSequence => e
      failed = e.failed.chars.split(//, 2)
      to_ascii_string = to_ascii_string.gsub(failed[0], '')
    retry
    end
   result.tr!(non_ascii, ascii)
  end
end
 

And we're done!

Just add the following line to your model:

 require 'acts_as_slugable_ascii'
 

and configure acts_as_slugable as instructed in README file. Now you're ready to face your users.

[Hint] Nice URLs with observe_field

For the last couple of weeks I've been working on a small Ruby on Rails application (and learning both Ruby and Rails in the same time). Since everyone's talking about the one-and-only Rails way, I wanted to make my little app as Rails as possible. That means that you have to write some workarounds to get things done the Rails way.

I wanted to add an onchange event to my collection_select. Normally you would put something like this in your template (that's what I did after browsing through a couple of tutorial/helps):

<%= collection_select(:story, :license_id, License.find(:all), :id, :name) %>
<%= observe_field('story_license_id', :frequency =>; 0, :update => 'story_license_description', :url => {:controller => 'license', :action => 'description'}, :with => '"id="+value') %>

This code however results in ugly URLs (ie. /license/description?id=1). If you want to keep URLs nice, use the following method:

<%= collection_select(:story, :license_id, License.find(:all), :id, :name) %>
<%= observe_field('story_license_id', :frequency => 0, :update => 'story_license_description', :url => {:controller => 'license', :action => 'description', :id => "'+value+'"}) %>
 

This way you'll keep your URLs beatiful throughout your application (ie. /license/description/1).

Oh, and forgive me the Rails way irony.

[Hint] Deploying Ruby on Rails applications on Dreamhost with Capistrano

So, you have a nifty Ruby on Rails application, got yourself a cheap Dreamhost account and you want to give Capistrano a try. And what do you get - errors.

This occurs if you have Ruby gems installed in your home directory (probably ~/.gems/). I'm assuming that you already have your environment configured. Despite that you still can get errors like no file to load -- json when executing cap deploy. That's because you have your shell configured, but most likely non-interactive environment is left intact.

To fix this you have to modify your .bashrc file (since you probably added GEM_HOME and GEM_PATH to .bash_profile):

export GEM_HOME=$HOME/.gems
export GEM_PATH=$GEM_HOME:/usr/lib/ruby/gems/1.8
 

And you're probably done.

You can also check these articles, they've been very helpful:
Deploying Rails (2.0) to Mongrel with Capistrano 2.1
Using Capistrano with Rails
Ruby on Rails - Dreamhost

[Hint] Detecting Firefox 3 in extensions

For a couple of months I've been developing a Firefox extension called BlipFox - an interface to Blip, Polish way-better-than-the-original-Twitter-clone. With Firefox 3 coming soon I had to get my extension ready for the final release.

Due to some chrome changes and bugs I had to update some code related to dynamic overlay rendering. Now all I need to know is whether the extensions is installed on Firefox 2 or Firefox 3, hence this snippet of code:

 
function isFF3()
{
	var foo = Components.classes["@mozilla.org/login-manager;1"];
	return typeof foo === 'function';
}
 

How do you like? Let me know if there are other methods of detecting Firefox 3 (more elegant, perhaps?).

[Hint] Using Windows keyboard in OS X Leopard

Since sometimes I'm awfully lazy - I got myself a second keyboard, just for the sake of not having to carry one to office and back. At home I use my beloved Apple Keyboard, while at work - a crappy, no-name, ten-bucks-a-piece one (it does it job, though!).

However, since at work I use a Windows keyboard I had a problem with the switched Option / Command keys. I've tried DoubleCommand, however I had to click through the System Preferences each time I switched my keyboard. Couple of minutes of googling and a nice script popped out. However, it was useless in OS X Leopard, so I made a couple of quick changes and here's something I came up with. First, switch-to-windows-keyboard script:

tell application "System Preferences"
	activate
	set current pane to pane "com.apple.preference.keyboard"
end tell
tell application "System Events"
	get properties
	tell process "System Preferences"
		click button "Modifier Keys…" of tab group 1 of window "Keyboard & Mouse"
		click pop up button 2 of sheet 1 of window "Keyboard & Mouse"
		delay 0.3
		click menu item 4 of menu 1 of pop up button 2 of sheet 1 of window "Keyboard & Mouse"
		delay 0.3
		click pop up button 1 of sheet 1 of window "Keyboard & Mouse"
		delay 0.3
		click menu item 3 of menu 1 of pop up button 1 of sheet 1 of window "Keyboard & Mouse"
		delay 0.3
		click button "OK" of sheet 1 of window "Keyboard & Mouse"
	end tell
end tell
tell application "System Preferences"
	quit
end tell

Second, back-to-apple-keyboard script:

tell application "System Preferences"
	activate
	set current pane to pane "com.apple.preference.keyboard"
end tell
tell application "System Events"
	get properties
	tell process "System Preferences"
		click button "Modifier Keys…" of tab group 1 of window "Keyboard & Mouse"
		click button "Restore Defaults" of sheet 1 of window "Keyboard & Mouse"
		click button "OK" of sheet 1 of window "Keyboard & Mouse"
	end tell
end tell
tell application "System Preferences"
	quit
end tell

You might ask why not use Ryan Block's script? Well, since I rely heavily on QuickSilver I also created two Platypus applications - one for each script. I'm a keyboard user, so I didn't like the clicking on the Macintosh / PC screen. ;-]

[Release] Fluid’s Google Reader Fix

For quite a few weeks I've been using Fluid with Google Reader - having a standalone WebKit based application with dock notification works great for me. However, one thing has been bothering me - Fluid never clears the dock badge when all the items are read. I've just posted a little fix for this issue - grab it at http://userscripts.org/scripts/show/23422.

Let me know if this works for you!

[Hint] Pasting content into Ext’s 1.1.1 HtmlEditor

Ext has it's up and downsides, but is indeed a great framework if you need out-of-the-box set of tools for complex layout development. However we've noticed that Ext's HtmlEditor has problems with content that is pasted into the textarea (we used Firefox 2.0 on both Linux and Windows - never happened on OS X systems). After pasting some HTML the getRawValue method returns a string that doesn't include the pasted content. This issue only occurs if after pasting the content HtmlEditor's textarea is not modified - inserting any character (including whitespace) will prevent it.

The workaround is easy - you need to use the method syncValue. An excerpt from Ext's documentation:

Protected method that will not generally be called directly. Syncs the contents of the editor iframe with the textarea.

Despite it being protected we can call it directly. If you're facing the problem of missing content then use the following snippet to read data from HtmlEditor:

YourHtmlEditor.syncValue();
var content = YourHtmlEditor.getStylesheet() + "\n" + YourHtmlEditor.getRawValue();

We use it in our webmail module and it works like a charm (finally!).

[Howto] Installing and using mysqldiff

What happens when you have to compare structures of two databases? Well, first thing - with all the great tools around (watch and learn: ActiveRecord's Migration) you shouldn't have let this happen. But since you're reading this you're probably already facing this problem.

We have a couple of options: Windows users could try SQLyog MySQL GUI - Enterprise Edition, PHP folks have MySQLdiff, but there's another thing you might want to try. It's my favourite since CocoaMySQL and command line client suit me well for every day work and I don't fell like spending $50 on a piece software that I might (or not) use once a year. I present to you mysqldiff (what a fancy name)!

I'm using OS X 10.5.1 with default Perl installation, so first I had to install the prerequisites - Class-MakeMethods. Download the package and from the command line execute:

tar xzf Class-MakeMethods-*.tar.gz
cd Class-MakeMethods-*
perl Makefile.PL
make test
sudo make install

Now on to the mysqldiff. After downloading we need need to execute the same set of commands:

tar xzf MySQL-Diff-*.tar.gz
cd Class-MakeMethods-*
perl Makefile.PL
make
sudo make install

And you're done - by default mysqldiff is installed to /Library/Perl/5.8.8/MySQL/mysqldiff.pl.

An example:

mysqldiff.pl --host=host.com --user=user --password=password database_1 database_2

This command will list all the queries required to recreate the exact structure of all the tables of the database_2 in database_1. You can also run mysqldiff.pl with --apply switch, but I always like to check the script before executing it.

That's it - if you know a better tool - be sure to let me and others know in the comments to this post.

[Hint] ISO 8601 compliant week numbers

Last week one of our customers noticed that the Calendar module in our CRM application shows incorrect week numbers. Since it was a really old module we were pretty sure that they were correct - isn't the PHP's date function supposed to do all the dirty work for us?

Later on we came up with Polish government's norm (suggestion, to be exact) that states that the first week of the year is the first week which has at least 4 days in the year (and since it's ISO 8601 compliant, our task was quite easy). We had to remove all the occurrences of:

  1. $var = date('W');

and replace them with:

  1. $var = strftime('%V');

since the result of strftime('%V') is ISO 8601 compliant.