Neverwinter Nights Diamond HotU rocks

It runs under GNU/Linux, so it must be pretty awesome. On my third time though Hordes of the Underdark, again as a cleric. That must be the most overpowered class in nwn. Based my third pass loosely on a cleric arcane archer build, but without the archer part.

Keeping in mind HotU only takes you through to about level 27, I adjusted accordingly. I love cleric spells, so I decided to go mostly full cleric with a level of bard. Specifically, clerics come with all the buffing spells, minus cat’s grace, greater magic weapon, darkfire, divine favor, divine power, word of faith, heal, harm, implosion, unholy aurora, storm of vengeance, and gate. (What’s not to love about a summoned Balor?)

Why bard? It’s a tumble class, like rogue and monk. Moreover, like rogue, bard has use magic device as a class skill. Finally, like the fighter classes, bard has discipline as a class skill.

The stats to 27 end up like (CBC is pretty awesome, but doesn’t work so well under OpenOffice.org):

STR 13
DEX 10
CON 10
WIS 16 (20)
INT 14
CHA 13 (16) (feat: Greater Charisma +1)

Bearing in mind the absurd quantity of magical equipment in HotU, my plan was to capitalize on the divine might feat, allowing me to bash away at stuff. Naturally, I can already harm stuff to death, but what fun is that?

Including ridiculous equipment, the stats further improve.

STR 25 (+12 equipment)
DEX 13 (+3 equipment)
CON 10
WIS 26 (+6 equipment)
INT 14
CHA 25 (+9 equipment)

Using umd to wear boots of the sunsoul +5 (+3 DEX, +5 dodge AC) and talisman of pure evil (+3 CHA, +3 WIS) is priceless. Gaining 4 AC from 20 ranks of tumble is pretty sweet too.

Naturally I’d like to further extol the virtues of nwn cleric, but I’ve spent all that time playing instead. Moreover, after 10 years it’s doubtful anyone still cares.

Three Invaluable PDF tools

While I’ve never been one to write top n things posts, there are three PDF tools I use routinely that are pretty awesome.

The most generally applicable is PDF Creator, which lets you print to PDF on any Windows system. It uses the magic of Open Source ™ internally, essentially ghostscript, to make it happen.

The second is, again for Windows, PDF-XChange Viewer. It’s free as in beer tool by Tracker Software Products. It will allow you to add annotations to existing PDF files, line art, text, and so forth. Useful for marking up PDF files with copy edits, perhaps when working with a desktop publisher or artwork designer.

The third is, oddly enough, an OpenOffice.org plugin. While I find OOo entirely lacking for many — most — tasks, Sun’s PDF import extension allows you to import PDF files and make changes. It’s awesome, as long as you don’t need to modify the original PDF file. The changes must be saved as part of an OOo draw file. Great for changes to PDF forms that are outdated, like a HSA employer contribution form for the prior year.

The second and third I use sparingly, but have found both essentially when needed.

Alex Leary at St Pete Times furthers Social Security disinformation

Leary disseminates the usual blather. “Many experts” agree with the need to raise the retirement age and reduce benefits. Only one “expert” is quoted, a conservative hack from the American Enterprise Institute, Andrew Biggs. Clearly, Leary is lazy.

Many Social Security experts agree that raising the retirement age is one of the solutions that must be considered if the 75-year-old entitlement program is to avoid insolvency.

“Any expert from any political spectrum will tell you that Rubio was right,” said Andrew Biggs, former No. 2 at the Social Security Administration and now with the conservative American Enterprise Institute for Public Policy Research.

ExtJS Combo, findRecord, and equality

ExtJS’ implementation of a combo box, ComboBox, is one of the more complicated components provided in all of ExtJS. It glues together TriggerField and DataView to allow for quite a few different uses. If you’re using both a nameField and a valueField, upon setValue a call is placed to findRecord to determine if there’s a proper display value available. findRecord is included from ExtJS 3.2.0 below:

    // private
    findRecord : function(prop, value){
        var record;
        if(this.store.getCount() > 0){
            this.store.each(function(r){
                if(r.data[prop] == value){
                    record = r;
                    return false;
                }
            });
        }
        return record;
    },

Notice the usage of the normal equality operator. In JavaScript, type coercion happens behind the scenes when using double equal. Often for Combo, that’s what you’d want. For example, if setValue is called with “51″ and the store record has an id field which is the integer 51, in JavaScript “51″ == 51.

Unfortunately, I have an application where I am sending back the integer 0 and in my store I have a record with an id of “” and another with an id of 0. With type coercion, 0 == “”, meaning only one of the two records can ever be selected, even though both will appear in the combo list.

Naturally, my resolution was to introduce triple equality, ===, to the findRecord function via Ext.override. Of course, when using the SuperSelectBox ux, I had forgotten that it splits setValue upon a delimiter, meaning findRecord is matching a bunch of strings with === and not coercing them to match my integer record ids.

Can’t have it both ways.

I’ll have to add my override to my particular extension of ComboBox that I use most places and leave the original setValue pristine.

Commenter Brian Peiris explains away the coercion confusion.

We can demonstrate JavaScript’s boolean coercion with a double NOT (!!) operator:

!!undefined === false
!!null === false
!!0 === false
!!” === false

Always valuable to remember!

Silent Hunter 5 DRM Requires Continuous Internet to Play

The just released Silent Hunter 5 — the long anticipated sequel to the buggy and disfunctional SH4 and SH3 titles — ships with a truly nasty copy protection sheme. Apparently, continuous authentication is required or the game disables itself, even during play.

UBIsoft servers are already experiencing outages, preventing a game owner from playing his legitimately purchased title. Can’t say I’m interested in such onerous DRM combined with a game that’s sure to be at least as buggy as the prior titles. What ever happened to trust, eh?

On the flip side, this likely resolves the longstanding issue of producers copying DVD titles to produce perfect ‘pirate’ DVDs.

It seems these wankers couldn’t program to save their lives. It’s already cracked. UBI also had to release a patch — just like with SH4 — the same day the game was released it’s so buggy. No surprise there. My wallet’s closed.

Thunderbird 3 Address Book Completely Hosed

The list management itself is entirely broken. Addresses can be added both under List properties and in the address book itself, but the two are not in sync. Addresses that appear in List properties are authoritative; those in the address book itself are not. There appears to be no relationship between the two or any reason why a list is simply not a free standing entity.

Simple things, like double clicking an address book with a list, will both expand the tree node and open the address book properties window. What’s more, why is a list a child node at all in the address book tree listing? It seems to serve no purpose whatsoever.

Adding entries to list properties — the authoritative source — when they already exist in the address book results in duplicate entires, until you click another address book and then return, removing the duplicates. Why?

Oh, lovely, now clicking on list child displays the list of active list email addresses. How entirely unintuitive, that it took an hour to deduce how this works. It was empty before, even with addresses added.

Very disturbing.

Also discovered Yahoo Mail disables inactive accounts. What a terrible service.

Exclusive favorite association for ActiveRecord has_many

It’s not uncommon in my application to maintain an exclusively preferred selection out of a collection on an object. For example:

class Person < ActiveRecord::Base
  has_many :employers, :class_name => 'Employee'
end
 
class Company < ActiveRecord::Base
  has_many :employees
end
 
class Employee < ActiveRecord::Base
  belongs_to :person
  belongs_to :company
 
  named_scope :favorite, {:conditions => {:favorite => true}}
  named_scope :without_me, lambda {|id|
    {:conditions => ['id NOT IN (?)', id]}
  }
 
  after_save :unique_favorite
end

Imagine there exists a possible principal employer and many ancillary employers. How then to allow for zero or one favorite employer, or email address, ect? (As opposed to weighting employers, perhaps by an integer value.) While likely not the most efficient or possibly even sane solution, I initially used the following after_save callback:

def unique_favorite
  person.employers(:reload)
  self.class.send(:with_scope, :find => {:conditions => 'favorite IS TRUE'}) do
    if person.employers.count > 0 and favorite?
      person.employers.reject {|myself| self == myself}.each do |employer|
        employer.update_attributes!({:favorite => false})
      end
    end
  end
end

First, the Person instance’s employers collection is reloaded. I found this necessary. Next, I define a scope and execute a count query on the employers association to determine if any favorites are already defined and if this employer instance is flagged as favorite.

If such is the case, all other employers with favorite set to true are set to false to ensure only one is true at a time.

Otherwise, nothing happens.

After an age, named_scope was introduced to ActiveRecord, making the following a cleaner callback:

def unique_favorite
  person.employers(:reload)
  if favorite? and person.employers.favorite.count > 0
    person.employers.without_me(self.id).each do |employer|
      employer.update_attributes!({:favorite => false})
    end
  end
end

Naturally, it can be taken a step further with an assist from PostgreSQL’s plpgsql stored procedure language.

-- Ensure favorite is true for only a single row
CREATE OR REPLACE FUNCTION employees_favorite() RETURNS TRIGGER AS $employees_favorite$
BEGIN
	IF (NEW.favorite = false) THEN
		RETURN NULL;
	END IF;
	UPDATE employees SET favorite = false WHERE id IN (
		SELECT t2.id FROM employees AS t1
		LEFT JOIN employees AS t2 ON t2.person_id = t1.person_id
		WHERE t1.id = NEW.id AND t2.favorite IS TRUE AND t2.id NOT IN (NEW.id)
	);
	RETURN NULL;
END;
$employees_favorite$ LANGUAGE plpgsql;
 
DROP TRIGGER IF EXISTS employees_favorite ON employees;
CREATE TRIGGER employees_favorite AFTER UPDATE OR INSERT ON employees
	FOR EACH ROW EXECUTE PROCEDURE employees_favorite();

Finally, the database functions need to be created somehow. A custom Rake task fits the fill, inspired by the excellent db-populate for seeding application data.

desc 'Load database functions stored in db/functions'
task :functions do
  require 'erb'
  require 'active_record'
  config = YAML::load(ERB.new(IO.read(File.dirname(__FILE__) + '/../../config/database.yml')).result)
  db_adapter = RAILS_ENV || 'development'
  db = ActiveRecord::Base.establish_connection(config[db_adapter])
  conn = db.connection()
  Dir[File.join(Rails.root, 'db', 'functions', '*.sql')].sort.each do |defs|
    conn.execute(File.read(defs))
    puts "Loaded #{File.basename(defs)}"
  end
end

Personally, I prefer the above approach — although database centric — because it DRYs up my models in a rather extremely sense. It’s also faster and absolutely guaranteed. I also prefer the simpler query, which asks the database for all other employee rows that share the same person, have favorite set to true, and do not have the primary key of the row for which the trigger is executing. Any rows returned from the subquery are then modified by the UPDATE statement to have favorite set to false.

Of course, both the ActiveRecord based Ruby centric approach and the PostgreSQL approach proceed from the assumption that the above logic is a good approach to solving the exclusive favorite amongst many choices problem I face.

It’s certainly easy to work with on the application side, as I can easily toggle any given item as a favorite and know it will be set exclusively. I use the same approach for email addresses, telephone numbers, and physical mailing addresses. (Yes, they’re all has_many associations in my application, not fixed column centric.)

Properties applied via Ext.extend are shared

I should know better, but recently I extended Ext.Panel and defined the plugins array as part of the object literal.

MyKlass = Ext.extend(Ext.Panel, {
 
  plugins:['cool', 'cooler'],
 
  initComponent:function() {
    MyKlass.superclass.initComponent.apply(this, arguments);
  }
});

That’s all well and good, except there’s an important consequence to the above. As mentioned in the documentation, in reference to the second argument to Ext.extend for the two parameter signature: A literal with members which are copied into the subclass’s prototype, and are therefore shared between all instances of the new class.

In short, it means every instance of MyKlass shares a reference to the plugins array, not its own copy. That can be a problem if you need to add or remove a plugin in instances or subclasses.

If you enjoy the above pattern, you can clone the array so a pristine copy is being modified. Below I use the Ext.ux.clone extension when extending my class above. The plugins array is still shared amongst all classes and subclasses, though.

newKlass = Ext.extend(MyKlass, {
 
  addPlugin:false,
 
  initComponent:function() {
    if(this.addPlugin) {
      this.plugins = Ext.ux.clone(this.plugins||[]).concat('new-plugin');
    }
    MyKlass.superclass.initComponent.apply(this, arguments);
  }
});

Another approach is to define the array in the constructor or within initComponent itself.

MyKlass = Ext.extend(Ext.Panel, {
 
  constructor:function(config) {
    // Each instance gets its own copy now -- the prototype does not have plugins applied to it
    if(this.plugins) {
      this.plugins = this.plugins.concat('cool', 'cooler');
    }
    else {
      Ext.apply(this, {plugins:['cool', 'cooler']});
    }
    MyKlass.superclass.constructor.call(this, config);
  }
});

Strings, Numbers, and Booleans aren’t a worry.

Misled by Embarq / Centurylink on bundle

Last July, I spoke with someone at Embarq about our bill. My goal was to reduce the monthly cost if possible. She was more than happy to help. Six months later, I notice all our packages — the multi-line, the DSL — are now 2 year bundles.

Naturally, I would never have signed up if the Embarq representative had said I was authorizing a two year commitment. I don’t believe in multi-year agreements. It limits options. So we’re now seemingly stuck with Centurylink until our “agreement” expires. What a scam.

Running VoIP on Brighthouse is looking more and more worthwhile all the time. It appears I have 18 months to plan.

Replace newlines with p tags in ExtJS DataView

Another fun usage of XTemplate with an assist from DataView’s prepareData function, let’s wrap each paragraph separated by two newlines in actual paragraph tags instead of using Ext.util.Format’s nl2br function, as BR tags cannot be styled.

var config = {
  xtype:'dataview',
  singleSelect:true,
  autoScroll:true,
  cls:'custom',
  overClass:'x-view-over',
  itemSelector:'div.wrap',
  prepareData:function(recordData, ri, record) {
    recordData.message = recordData.message.split(/\n{2,2}/);
    return recordData;
  },
  tpl:new Ext.XTemplate(
    '<tpl for=".">',
    '<div class="wrap">',
    '<blockquote>',
      '<tpl for="message"><p>{.}</p></tpl>',
    '</blockquote>',
    '<cite>',
    '<span class="author">&mdash; {author}</span> @ <span class="date">{when}</span>',
    '</cite>',
    '<div class="x-clear"></div>',
    '</div>',
    '</tpl>',
    '<div class="x-clear"></div>')
};

Above, I replace the message property’s value with an array split on the expression above. Then, I can use utilize a child template and XTemplate’s magic dot operator when looping over the message property array. Now, I have nicely wrapped text I can easily style instead of a bunch of double BR tags.