Written on
December 7, 2007
by
Chris Heald
I upgraded to Rails 2.0 today, and found that countercache columns were suddenly :readonly, which means that you can’t update them with your regular old model.associationcount = model.association.count routine. So, I whipped up the following ActiveRecord patch.
module TagTeamInteractive
module ActiveRecordSynchCounters
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def synch_all_counters!
self.find(:all).each do |u|
u.synch_counters!
end
end
end
module InstanceMethods
def synch_counters!
fields = {}
self.attributes.each do |k, v|
if k.match /_count$/ then
begin
f = k.gsub(/_count$/, "")
assoc = self.class.reflect_on_association(f.to_sym)
if assoc.macro == :has_many then
fields[f.to_sym] = self.send(f.to_sym).count - v
else
next
end
rescue
next
end
end
end
self.class.update_counters(id, fields) unless fields.empty?
end
end
end
end
ActiveRecord::Base.send(:include, TagTeamInteractive::ActiveRecordSynchCounters)
ActiveRecord::Base.send(:include, TagTeamInteractive::ActiveRecordSynchCounters::InstanceMethods)
Easy enough. Load that in your environment.rb, and then you can use the following:
Dir.glob("#{RAILSROOT}/app/models/*.rb”).each do |file|
m = File.basename(file).split(”.”).first.camelize.constantize
synchallcounters! if m.isa? ActiveRecord::Base
end
That’ll run synchallcounters! for each record on each model in your database. Instant counter re-synching across the entire database. Tada!
Posted in Ruby/Rails
|
1 Comment » | View blog reactions
Written on
June 15, 2007
by
Chris Heald
Script used:
Download it in AddOn form at http://wow.tachyonsix.com/GhettoFPSMonitor.zip
FPSProfiler = CreateFrame("Frame", "FPSProfiler", UIParent)
local runTime = 0
local fpsSum = 0
local fpsTicks = 0
local startTime = 0
local timeToRun = 0
local profilerLock = false
local function print(msg, ...)
DEFAULT_CHAT_FRAME:AddMessage(string.format("|cffffcc00GhettoFPSProfiler:|r " .. msg, ...))
end
local function report()
local endTime = GetTime()
print("Profiling finished!")
print("Total run time: %s", runTime)
print("Total run time [GetTime() method]: %s", endTime - startTime)
print("Total frames: %s", fpsTicks)
print("Average framerate: %s", fpsSum / fpsTicks)
profilerLock = false
FPSProfiler:SetScript("OnUpdate", nil)
end
-- Runtime of doProfiling is O(n)
local function doProfiling()
runTime = runTime + arg1
fpsTicks = fpsTicks + 1
fpsSum = fpsSum + GetFramerate()
if runTime > timeToRun then
report()
end
end
FPSProfiler.Start = function(runFor)
if profilerLock then
print("Profiling, can't start another profile.")
return
end
profilerLock = true
timeToRun = runFor
runTime = 0
fpsSum = 0
fpsTicks = 0
if _G.klhtm then
print("Starting profiling, running for %s seconds, KTM is |cff00ff00enabled|r", runFor)
else
print("Starting profiling, running for %s seconds, KTM is |cffff0000not enabled|r", runFor)
end
startTime = GetTime()
FPSProfiler:SetScript("OnUpdate", doProfiling)
end
System Specs:
- Athlon X2 3800+ Dual Core, 2.01GHz
- 2.0 GB DDR3200 RAM
- BFGTech NVidia Geforce 7600GT, nv4_disp.dll driver: 6.14.0010.9371 (English)
- Windows XP SP2, build 2600
- AMD Cool ‘n Quiet driver not installed, Cool ‘n Quiet not enabled in the BIOS.
Process list:
- calc.exe
- taskmgr.exe
- wmiprvse.exe
- WoW.exe
- TSVNCache.exe
- Photoshop.exe
- notepad++.exe
- winamp.exe
- firefox.exe
- gain.exe
- avnotify.exe
- wuauclt.exe
- svchost.exe
- wscntfy.exe
- cftmon.exe
- jusched.exe
- svnhost.exe
- nvsvc32.exe
- btwdins.exe
- avguard.exe
- sched.exe
- spoolsv.exe
- explorer.exe
- avgnt.exe
- lsass.exe
- services.exe
- winlogon.exe
- csrss.exe
- smss.exe
- alt.exe
WoW Settings:
- Resolution: 1680×1050 (Wide), Windowed Fullscreen, running at 1920×1200 native resolution
- 60hz refresh
- 24 bit color, 24 bit depth 2x multisample
- Terrain distance: 60%
- Environment detail: 50%
- Anisotropic filtering: 0%
- Terrain texture: 100%
- Texture detail: 100%
- Character Shadows: on
- Spell Detail level: 100%
- Weather Intensity: on
- Level of Detail: off
- All shaders checked
- Trilinear filtering: off
- Vertical sync: off
- Triple buffering: off
- Cinematic subtitles: off
- Hardware cursor: on
- Smooth mouse: off
- CPU Profiling: Off

Location: Azure Watch
Addons running:
- GhettoFPSMonitor
- KLHThreatMeter v 19.18 (for test 2)
Testing methodlogy
Game was loaded. A UI reload was invoked, and the UI was given at least 20 seconds to fully load and get all work out of the way and to enter an idle state. The profiler was invoked, and I physically got up from the computer and walked away to ensure that there would be no input that would incur additional overhead processing costs. There was additional rendering cost as players ran past my view, but they occurred in both tests.
This ensured that the tests occurred on a stripped-down, bare-bones, otherwise idle system with as many variables as possible held constant.
15 sec run time:
/script FPSProfiler.Start(15)
No KTM
- Run 1: 42.01
- Run 2: 41.46
- Run 3: 40.90
- Run 4: 40.57
Average: 41.24 FPS

KTM
- Run 1: 40.23
- Run 2: 39.67
- Run 3: 37.68
- Run 4: 37.82
Average: 38.85 FPS

Conclusion: Over a 15 second period, KTM degraded my overall system performance by 5.8%
120 sec run time:
/script FPSProfiler.Start(120)
No KTM
Run 1: 41.47

KTM
Run 1: 39.23

Conclusion: Over a 120 second period, KTM degraded my overall system performance by 5.5%
Final notes
10 tests is hardly conclusive (the one test each of the 120 sec cases is particularly incomplete), but I did it on a very short time budget, so more profiling may be done at a later date. I’ve included my entire setup here for anyone to replicate at will, and I am confident that it will produce similar results.
Edit: For grins and giggles, I went ahead and profiled Omen and ThreatLib under the same conditions.
Stock, 3 tests, 15 sec
- 41.759 + 41.55 + 40.91 = 41.40
Omem, 3 tests, 15 sec
- 42.22 + 40.92 + 40.44 = 41.19
Result: System performance while idle was reduced by 0.51% with Omen and ThreatLib versus a stock system, which is likely easily explained by the margin of error resulting from the low number of tests. I took screenshots, but I’m feeling lazy and anyone can replicate the tests as they see fit.
Posted in Gaming, World of Warcraft, AddOns
|
3 Comments » | View blog reactions
Written on
May 3, 2007
by
Chris Heald
I got my latest commit of SanityBags into SVN last night (at about…oh, 1:30 AM). It’s a massive overhaul of the whole system, and much more closely obeys the MVC design objectives now.
SanityBags is a virtual bags AddOn for World of Warcraft interfaces. I started writing it…oh, 5 months ago, and then got busy and never actually finished it. It’s still unfinished, but it’s a lot further along now.
Read the rest of this entry »
Posted in Software Development, Gaming, World of Warcraft, Lua, Sanity, SanityBags
|
3 Comments » | View blog reactions
Written on
May 2, 2007
by
Chris Heald
Yesterday, I discovered RubyInline. In short, it’s a gem that allows you to write Ruby extensions in C.
I decided to see what I could do with it.
Read the rest of this entry »
Posted in Ruby/Rails, Software Development, Hacks
|
No Comments » | View blog reactions
Written on
May 1, 2007
by
Chris Heald
My first Rails plugin is working! I won’t quite say finished, as it includes no formal tests or migrations, but I’ll get that packaged up in due time.
I recently found myself wanting to move my MySQL database from MyISAM tables to InnoDB tables. The primary reasons for this are:
- InnoDB supports transactions
- InnoDB supports row-level locking, as opposed to table-level locking
- InnoDB supports foreign key constraints
All very, very good things to have. Unfortunately, InnoDB does not support MyISAM’s FULLTEXT indexes. For those of you unfamiliar with MySQL’s fulltext searching, it’s a really slick little piece of work, especially the boolean mode fulltext searches. I wanted to have this functionality available across my database, but it seemed I couldn’t have my transactional, row-level constrained cake and search it, too.
Enter acts_as_fulltext_indexed.
Read the rest of this entry »
Posted in Ruby/Rails, Software Development
|
17 Comments » | View blog reactions
Written on
April 27, 2007
by
Chris Heald
BorkWeb has a good article about a relatively old and yet, underused image technique. It’s a good read, and certainly worth implementing where possible. I tend to use this technique with image rollovers, since you have “sets” of images that belong together.
It’s worth noting that this technique is very similar to sprite sheets used in most 2D games, and UV-mapped texture maps used in 3D games. Rather than creating a separate resource for each frame of your sprite, or for each portion of a model you want to texture, you create one single resource for all the frames/textures, load it a single time, and then display portions of it as necessary. The benefits are the same in games and on the web - fewer resources to load means faster load times, and it’s cheaper to display two parts of one image than it is to display one part of two images.
Posted in CSS
|
No Comments » | View blog reactions
Written on
April 24, 2007
by
Chris Heald
There are all these great logos out there for the so-called “Web 2.0″ companies springing up every 15 minutes. How do they do it? It’s easier than you may think.
I’m going to show you how to create one of these logos in Photoshop in 5 minutes, or your money back. So fire up Photoshop, think of a name, and let’s get cracking.
Read the rest of this entry »
Posted in Design, Photoshop
|
No Comments » | View blog reactions
Written on
April 24, 2007
by
Chris Heald
One of the annoying things about Rails is that it goes so far to connect pieces of the database together for you, but it doesn’t have the ability to automatically do validations or associations based on the schema data. A VARCHAR(30) NOT NULL is always going to need validates_length_of(:field, :in => 0..30), and validates_presence_of(:field), right? Or if you define a foreign key on a table, that’s an implied association that you’d have to set up in your model.
Dr. Nic’s Magic Models provide just that. Based on the DB schema, the models will get automatic validations and associations, which I suspect would reduce the amount of code you have to write, and would result in both more complete Ruby code and better DB schemas.
Posted in Ruby/Rails
|
No Comments » | View blog reactions
Written on
April 24, 2007
by
Chris Heald
RJS is a great little tool for interactively updating pieces of a page in response to some AJAX action, but it tends to fall short when you want to do anything beyond blinding dumping content into a page. Dan Webb has written a plugin called “MinusR” that basically flips RJS templates on their head. Instead of using Ruby generators to generate Javascript statements, you can write plain Javascript and are given a helper method to dump any Ruby statement to a properly escaped and encoded Javascript string.
Read the rest of this entry »
Posted in Ruby/Rails, Javascript/AJAX
|
No Comments » | View blog reactions
Written on
April 23, 2007
by
Chris Heald
When proxying requests out to a load balancer, it’s often useful to avoid proxying some things. In the case of a Rails app, there is no need to proxy requests for images, JS, CSS, etc out to the Mongrel instances - Apache is perfectly capable of handling those itself.
There are several methods of doing this - one is to use RewriteRules to determine which URLs to not rewrite, but this is often more power than we need, and can be rather slow, depending on your implmentation. Fortunately, there’s a better way.
Read the rest of this entry »
Posted in Apache
|
No Comments » | View blog reactions