Thursday, March 10, 2011

Running .desktop files from the command line

After making a few .desktop files with custom commands in them (e.g.RXVT settings), it is useful to be able to run those same commands from the occasional terminal.

When KDE is installed, the kioclient can be used for this purpose:

kioclient exec file:/PATH_TO_DESKTOP_FILE

Custom desktop files are stored in ~/.local/share/applications, so this is easy to wrap in a shell function for inclusion in ~/.bashrc :

xdg-exec () {
    kioclient exec file:${HOME}/.local/share/applications/${1}.desktop
}

Example:

bash$ xdg-exec rxvt-unicode

Thursday, March 3, 2011

Ruby, Qt4, and AbstractItemModel

There are no good examples of using QAbstractItemModel in Ruby. For most purposes, QStandardItemModel will suffice. In this particular case, which calls for a lazily-loaded tree, QStandardItemModel will not cut it.

What follows is a simple implementation. The number of columns is limited to 1, items are read-only, there is no DnD support, and the model only supports the Display role for item data. Needless to say, these features are easy enough to implement, and would only distract from the example of subclassing Qt::AbstractItemModel.

The Model

=begin rdoc
A basic tree model. The contents of all tree nodes are determined by the ModelItems, not the Model, so they may be loaded lazily.
=end
class Model < Qt::AbstractItemModel
  signals 'dataChanged(const QModelIndex &, const QModelIndex &)'

=begin rdoc
The invisible root item in the tree.
=end
  attr_reader :root

  def initialize(parent=nil)
    super
    @root = nil
  end

=begin rdoc
Load data into Model. This just creates a few fake items as an example. A full implementation would create and fill the top-level items after creating.
Note: @root is created here in order to make clearing easy. A clear() method just needs to set root to ModelItem.new('').
=end
  def load()
    @root = ModelItem.new('',nil)
    ModelItem.new('First', @root)
    ModelItem.new('Second', @root)
  end

=begin rdoc
This treats an invalid index (returned by Qt::ModelIndex.new) as the index of @root.
All other indexes have the item itself stored in the 'internalPointer' field.

See AbstractItemModel#createIndex.
=end
  def itemFromIndex(index)
    return @root if not index.valid?
    index.internalPointer
  end
 
=begin rdoc
Return the index of the parent item for 'index'.
The key here is to treat the invalid index (returned by Qt::ModelIndex.new) as the index of @root. All other (valid) indexes are generated by AbstractItemModel#createIndex. Note that the item itself is passed as the third parameter (internalPointer) to createIndex.

See ModelItem#parent and ModelItem#childRow.
=end
  def index(row, column=0, parent=Qt::ModelIndex.new)
    item = itemFromIndex(parent)
    if item
      child = item.child(row)
      return createIndex(row, column, child) if child
    end
    Qt::ModelIndex.new
  end

=begin rdoc
Return the index of the parent item for 'index'.
This is made a bit complicated by the fact that the ModelIndex must be created by AbstractItemModel.

The parent of the parent is used to obtain the 'row' of the parent. If the parent is root, the invalid Modelndex is used as usual.
=end
  def parent(index)
    return Qt::ModelIndex.new if not index.valid?

    item = itemFromIndex(index)
    parent = item.parent
    return Qt::ModelIndex.new if parent == @root

    pparent = parent.parent
    return Qt::ModelIndex.new if not pparent

    createIndex(pparent.childRow(parent), 0, parent)
  end

=begin rdoc
Return data for ModelItem. This only handles the case where Display Data (the text in the Tree) is requested.
=end
  def data(index, role)
    return Qt::Variant.new if (not index.valid?) or role != Qt::DisplayRole
    item = itemFromIndex(index)
    item ? item.data : Qt::Variant.new
  end

=begin rdoc
Set data in a ModelItem. This is just an example to show how the signal is emitted.
=end
  def data=(index, value, role)
    return false if (not index.valid?) or role != Qt::DisplayRole
    item = itemFromIndex(index)
    return false if not item
    item.data = value.to_s
    emit dataChanged(index, index)
    true
  end
    
  alias :setData :data=

=begin rdoc
Delegate rowCount to item.

See ModelItem#rowCount.
=end
  def rowCount(index)
    item = itemFromIndex(index)
    item ? item.rowCount : 0
  end

=begin rdoc
Only support 1 column
=end
  def columnCount(index)
    1
  end

=begin rdoc
All items can be enabled only.
=end
  def flags(index)
    Qt::ItemIsEnabled
  end

=begin rdoc
Don't supply any header data.
=end
  def headerData(section, orientation, role)
    Qt::Variant.new
  end
end

The ModelItem

=begin rdoc
An example of a ModelItem for use in the above Model. Note that it does not need to descend from QObject.

The ModelItem consists of a data member (the text displayed in the tree), a parent ModelItem, and an array of child ModelItems. This array corresponds directly to the Model 'rows' owned by this item.
=end
class ModelItem
  attr_accessor :data
  attr_accessor :parent
  attr_reader :children

  def initialize(data, parent=nil)
    @data = data
    @parent = parent
    @children = []
    parent.addChild(self) if parent
  end


=begin rdoc
Return the ModelItem at index 'row' in @children. This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.
=end
  def child(row)
    @children[row]
  end


=begin rdoc
Return row of child that matches 'item'. This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.
=end
  def childRow(item)
    @children.index(item)
  end


=begin rdoc
Return number of children. This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.
=end
  def rowCount
    @children.size
  end


=begin rdoc
Used to determine if the item is expandible.
=end
  def hasChildren
    childCount > 0
  end


=begin rdoc
Add a child to this ModelItem. This puts the item into @children.
=end
  def addChild(item)
    item.parent=self
    @children << item
  end
end

This should serve as a basic implementation of an AbstractItemModel.

Realistically, ModelItem would be subclassed to represent different types of items in the data source, each of which would also (likely) be subclassed from ModelItem. This allows a browsable tree to be created for navigating data hierarchies.

Tuesday, February 8, 2011

The way it goes

Toy Code : Working source code which is extensively peer-reviewed, obsessively tested, well-documented, and released once. Often used in articles, examples, mailing lists, and blog posts/comments.

Production Code: Working source code which is rarely reviewed, infrequently tested, poorly documented, and regularly released. Responsible for the majority of the world's electronic infrastructure.

Sunday, January 16, 2011

Ruby: Array of Hashes to 2-D Array

Quick Ruby trick.

It is often useful to convert an Array of Hashes (representing a table of objects) to an Array of column names (table header) and an Array of rows (table data). The canonical example would be taking the result of a DB query (ala Sequel) and displaying it in an HTML table (ala DataTables).

Without further ado, here is a proper map/inject one-liner:

array.inject( [data.first.keys, []] ) { |memo, row| memo[1] << memo[0].map { |key| row[key] } ; memo }


A 2-dimensional Array is returned. The first dimension contains the column names, and the second contains the row data (in the same order as the column names, which is the tricky part as Hash#keys can return the keys in any order).

The columns can be sorted (or otherwise ordered specifically) by manipulating data.first.keys when it is initially stored in memo[0].

Example of usage:

irb > data = [ { a: 1, b: 2, c: 3 }, { c: 9, b: 8, a: 7 } ]
irb >  cols, rows = data.inject( [data.first.keys, []] ) { |arr, row| arr[1] << arr[0].map { |key| row[key] } ; arr }
irb > cols
 => [:a, :b, :c]
irb > rows
 => [[1, 2, 3], [7, 8, 9]]

Thursday, January 6, 2011

QScintilla and getSelection()

The recent Ubuntu upgrade and subsequent Ruby woes were caused, of course, by the desire to install libqscintilla-ruby, a package only available on 10.10 (and even then, only for 1.8).

For the most part, QScintilla works fine in Ruby... until one encounters methods like this:

void getCursorPosition(int *line, int *index) 

void getSelection(int *lineFrom, int *indexFrom, int *lineTo, int *indexTo)


The docs provide some hint as to the problem:

If there is a selection, *lineFrom is set to the line number in which the selection begins and *lineTo is set to the line number in which the selection ends. (They could be the same.) *indexFrom is set to the index at which the selection begins within *lineFrom, and *indexTo is set to the index at which the selection ends within *lineTo. If there is no selection, *lineFrom, *indexFrom, *lineTo and *indexTo are all set to -1.

How does one pass an integer by reference in Ruby?

The answer: one doesn't. These functions take integer arguments and return nil, making them entirely useless in Ruby.

The Python guys did it right:

line_fro, idx_fro, line_to, idx_to = getSelection

The Ruby guys, of course, were lazy, and routed all calls directly to libqscintilla.so regardless of the sanity of their argument lists.


There is a way to make things work, however, thanks to ScintillaBase.


This, the base class of the Scintilla widget, provides the following method:

long SendScintilla(unsigned int msg, unsigned long wParam=0, long lParam=0)

At the top of  the base class documentation are a bunch of enums that look promising:

...
 SCI_SETSELECTIONSTART = 2142,   
 SCI_GETSELECTIONSTART = 2143,
 SCI_SETSELECTIONEND = 2144,  
 SCI_GETSELECTIONEND = 2145, 
...


Sure enough, these turn out to be the values for the msg parameter.


It makes for short work to add the following methods to the Scintilla object:


def get_sel_start
    # SCI_GETSELECTIONSTART
    self.SendScintilla(2143), 0, 0)
end

def get_sel_end
   # SCI_GETSELECTIONEND
    self.SendScintilla(2145), 0, 0)
end

def get_current_pos
    # SCI_GETCURRENTPOS
    self.SendScintilla(2008), 0, 0)
end


Yes, the messages have to be passed by number, as the symbols for the bulk of the messages have not been defined as constants in Ruby (lazy! bad! lazy!), as can be determined by examining Qsci::ScintilaBase.constants.

Monday, January 3, 2011

Ubuntu getting crappier and crappier

Stupidly made the decision to upgrade to 10.10 (for a single package!) on the main laptop (mbpro 5,5) and poof! No wireless!

Seriously, it's been three releases since an ethernet cable was required to do an upgrade.

Piece.
Of.
Shit.

Only question now is how many hours of productivity are going to be lost fixing what *was* a perfectly working system before the upgrade.

The sad fact is that os x and windows are just as unreliable for upgrades (while being less usable), and FreeBSD refuses to even glance at this hardware.

A decade ago, this shit used to *work*!

Running log of the fixes:

* apt-get install gnome-icon-theme to get NetworkManager running again. There is no fix for Wicd; it has apparently "stopped working".

* apt-get install gnome-alsa-mixer to un-mute the sound. Kmixer has been reduced from 7 or so channels to 1 (without a mute option).

* uninstall ruby 1.9 via apt-get. Download and install rvm (system-wide as this is a workstation), then do 'sudo rvm --default use 1.9.2' to make 1.9 the system-wide default (might roll that back later).

* reinstall all 1.9 gems.

* rebuild the passenger apache module ('sudo passenger-install-apache2-module') and update the config files to point to the new location.

* fix all rack-based webapps to include the line
    $: << File.dirname(__FILE__)
in config.ru and in each Sinatra::Base application file, as something got screwed in the ruby-passenger-rack environment, and PassengerRoot is no longer in the Ruby module path.

See? A smooth, seamless upgrade! You almost don't even notice that it happened!

Thursday, December 16, 2010

Apache + Passenger + Sinatra

OK, this seems like a pretty normal situation, and like most normal situations it apparently never occurs in nature (judging by the available docs).

An existing Apache webserver is to have a new webapp added to it, in a subdirectory of the DocumentRoot. Sinatra is the framework to be used, and Passenger is going to route requests from Apache to Sinatra without going through any mod_proxy or mod_rewrite business.

Assume the following: Apache2, an Ubuntu system, and all of the relevant gems have been apt-get installed. The web server root directory is /var/www, and the webapp will be in /var/www/timon.

First, update the Apache config file (/etc/apache2/apache2/conf):

<VirtualHost *:80>
  ServerName Apemantus
  DocumentRoot /var/www

  <Directory />
    Options -Indexes
    AllowOverride None
  </Directory>

  <Directory /var/www/>
    AllowOverride AuthConfig
    Order allow,deny
  </Directory>

  SetEnv RUBYLIB '/var/www/timon'
  PassengerEnabled on
  PassengerAppRoot /var/www/timon
  RackBaseURI /timon

</VirtualHost>

The bold lines indicate what must be added.
  • SetEnv : This just allows the code in subdirectories of the webapp to be easily required.
  • PassengerEnabled : Seems fairly obvious.
  • PassengerAppRoot : The full filesystem path to the webapp directory.
  • RackBaseURI : The relative (to DocumentRoot) path to the webapp directory.

Next, verify that the Passenger options (/etc/apache2/mods-enabled/passenger.conf) are correct:


<IfModule mod_passenger.c>

  PassengerRoot /usr
  PassengerRuby /usr/bin/ruby
  PassengerMaxPoolSize 10
  PassengerDefaultUser www-data


</IfModule mod_passenger.c>

These options are pretty straightforward. The only thing to note is that PassengerRuby can be set to a specific version of Ruby, e.g. jruby or ruby1.9.

Now, create an empty Rack-friendly directory structure for the webapp:

bash# cd /var/www
bash# mkdir timon
bash# mkdir timon/public
bash# mkdir timon/tmp

The use of public for static pages and tmp for restart.txt is well-documented.

Next, a Rack config file must be provided. This will be named config.ru (/var/www/timon/config.ru) and will have the following contents:

#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'

# Disable Sinatra's default Webrick instance
set :run, false

# Include timon.rb, the main webapp script
require 'timon'
run Sinatra::Application

Finally, the app itself must be provided. This will be named timon.rb (/var/www/timon/timon.rb) and will have the following contents:

#!/usr/bin/env ruby

require 'rubygems'
require 'sinatra'

get '/' do
  "<b>TIMON!</b>"
end

# Local 404 handler
not_found do
  "Timon NotFound exception"
end

# Local error handler
error do
  "Timon Error: " + env['sinatra_error'].name
end

Debugging can be made a bit more straightforward by adding some basic logging code to config.ru:


#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'

# Disable Sinatra's default Webrick instance
set :run, false

# Local logging
FileUtils.mkdir_p 'log' unless File.exists?('log')
log = File.new('log/sinatra.log', 'a')
$stdout.reopen(log)
$stderr.reopen(log)

# Include timon.rb, the main webapp script
require 'timon'
run Sinatra::Application

That's it.

Nothing much to it, really, but the lack of RackBaseURI in the relevant examples really makes debugging this kind of thing difficult.