<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Graham King &#187; Software</title>
	<atom:link href="http://www.darkcoding.net/category/software/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.darkcoding.net</link>
	<description>Solvitas perambulum</description>
	<lastBuildDate>Fri, 30 Jul 2010 19:57:10 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Notes from O&#8217;Reilly&#8217;s Website Optimization</title>
		<link>http://www.darkcoding.net/software/notes-from-oreillys-website-optimization/</link>
		<comments>http://www.darkcoding.net/software/notes-from-oreillys-website-optimization/#comments</comments>
		<pubDate>Sat, 05 Jun 2010 03:59:46 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[book]]></category>
		<category><![CDATA[business]]></category>
		<category><![CDATA[marketing]]></category>
		<category><![CDATA[review]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=783</guid>
		<description><![CDATA[These are my notes from O&#8217;Reilly&#8217;s Website Optimization. It is a strange book, that will appeal to the one-renaissance-person web business: someone who optimizes Javascript for performance and tracks Google Ads conversion goals. You&#8217;re bound to find a useful chapter in here, but I doubt you will find more than one relevant to you. Here [...]]]></description>
			<content:encoded><![CDATA[<p>These are my notes from <a href="http://oreilly.com/catalog/9780596515089">O&#8217;Reilly&#8217;s Website Optimization</a>. It is a strange book, that will appeal to the one-renaissance-person web business: someone who optimizes Javascript for performance and tracks Google Ads conversion goals. You&#8217;re bound to find a useful chapter in here, but I doubt you will find more than one relevant to you. Here are my notes:</p>

<hr />

<h1>Website Optimization. Conversion Rate Optimization</h1>

<p>The art and science of persuading your site visitors to take actions that benefit you.</p>

<h2>Crediblity</h2>

<p>Site must appear credible: Often a gut reaction.<br />
<a href="http://www.uky.edu/~drlane/capstone/persuasion/sourcecred.htm">Source credibility theory</a>: Perceived expertise and trustworthiness.</p>

<p><span id="more-783"></span></p>

<ul>
<li>Professional web design</li>
<li>Intuitive navigation (good usability)</li>
<li>Clear unique selling point</li>
<li>Loads fast</li>
<li>Use &#8216;trigger words&#8217; that users are scanning for</li>
<li>Benefit-oriented content, that focuses on visitors needs and goals</li>
</ul>

<p>Reassure users with:</p>

<ul>
<li>privacy policy link</li>
<li>trust logos: Verisgn / Better Business Bureau / etc</li>
</ul>

<h2>Six psychological principles of persuasion, use them:</h2>

<ul>
<li>social proof: Testimonials of people like you. Popularity (best selling xyz).</li>
<li>reciprocity: Give first, initiate.</li>
<li>consistency / commitement: Foot-in-the-door</li>
<li>liking / similarity: develop user personas, speak their language</li>
<li>authority</li>
<li>scarcity: list how many products left, how long till sale is over</li>
</ul>

<p>These come from <a href="http://www.darkcoding.net/behaviour/influence-by-robert-cialdini/">Robert Cialdini&#8217;s Influence</a></p>

<h1>Media</h1>

<p>Brand is: Company name, logo, and tagline. Make them good.</p>

<p>Hero shot: Include one high quality prominent picture of your product, or of someone enjoying it. Put image on left and text on right for easier reading. Make it relevant. Make image clickable for larger view. Give it a strong caption &#8211; headlines and captions are most read part of page.</p>

<p>Interactivity increases conversions: Video, animation, live chat, something the user can interact with. Be subtle and tasteful.</p>

<h2>Personas:</h2>

<ul>
<li>Demographics: Age, gender, income, education</li>
<li>Psychographics: Personality type</li>
<li>Geographic: Local, national, international?</li>
<li>Customer pain points and goals: What is the problem they are trying to solve? What is the single most important factor to them?</li>
<li>Value proposition / unique selling points: Why you and not the competition?</li>
<li>Benefit hierarchy: What benefits / features of your product are important to the customer? Order them by importance.</li>
<li>Key frustrations and objections: What is their biggest frustration with companies in your industry and/or products like yours?</li>
<li>Buying criteria: What factors do they weight when making the buying decision?</li>
<li>Risk reversal: What risks are your customers taking when buying your product, and can you avoid those? 100% money back guarantee, free trial, free return shipping, etc.</li>
<li>Keyword phrases: How do your customers refer to your product, what words do they use to search for it?</li>
</ul>

<p>Unique Selling Proposition: &#8220;Oh I&#8217;ve heard of you, you&#8217;re the company that &#8230;&#8221;</p>

<h2>Write for the web:</h2>

<ul>
<li>Brief. Bold. Inject personality. </li>
<li>Active voice, start with a verb.</li>
<li>Front load with keywords, users scan the first one or two words of each title / link / bullet.</li>
<li>Think newspaper headline.</li>
<li>Use sub-headings.</li>
<li>Write for scanning: Half the words. One idea per paragraph. Bullet lists. Highlight keywords.</li>
</ul>

<p>Develop primary and secondary responses:</p>

<ol>
<li>Primary response is your main call to action, typically &#8216;Buy Now&#8217;.</li>
<li>Secondary response is something for people who are not quite ready to buy now. Don&#8217;t lose them. Instead get them to sign up for newsletter, or download a PDF brochure, etc.</li>
</ol>

<p>Show don&#8217;t tell. Encourage customer&#8217;s emotional mental imaging.</p>

<p>Links get highest clickthrough at 7 &#8211; 12 words.
Repeat keywords that brought users to your site (ad words) in the links.</p>

<p>Avoid jargon, hype, and wimpy could should may might. Be positive &#8211; this will <em>absolutely</em> happen as soon as they give their credit card number!</p>

<p>Always be closing: Actually ask them to buy. Ask them to perform the call to action.</p>

<h1>Metrics</h1>

<h2>Conversion metrics</h2>

<p>What are your objectives &#8211; what does a &#8216;conversion&#8217; mean?</p>

<ul>
<li>Sell product: Count completed orders, count additions to shopping basket.</li>
<li>Ad clicks: For ad-supported site.</li>
<li>Distribution / Downloads: PDF, software, etc.</li>
<li>Informational: Read a page, view a video.</li>
<li>Comments: A blog could make getting a user to comment a conversion.</li>
<li>Sign-ups: Joining the site. Registering for the newsletter or for a beta invite, etc.</li>
</ul>

<p>What to track, what to care about:</p>

<ul>
<li>Unique visitors.</li>
<li>Average time on site. Pages per visit. These measure how engaged the user was in your site.</li>
<li>Bounce rate: % of users who left within a short period of time, and without browsing to a different page on your site. High bounce rate pages need some work, or the campaign that lead them there incorrectly needs some work (wrong adwords being targetted for example)</li>
<li>Conversion rates: Whatever you define a conversion to be.</li>
<li>Entry points: Which pages do your users first land on? Also called &#8216;primary content consumption&#8217;.</li>
<li>Exit points: Where do users stop browsing and leave? If it&#8217;s the &#8216;payment complete&#8217; page, fine. If not, needs looking into.</li>
<li>ROI (return on investment) by keyword and campaign, an cost per conversion: Track your advertising spend&#8217;s effectivness.</li>
</ul>

<p>Business metrics you should be tracking anyway:</p>

<ul>
<li>Revenue per order</li>
<li>Profit per order</li>
</ul>

<h2>Performance metrics:</h2>

<ul>
<li>Number of HTTP requests</li>
<li>Total size of data needed to view page</li>
<li>Load times with empty cache and with full cache:
<ul><li>Server processing time</li>
<li>Start render &#8211; until then page is blank</li>
<li>Onload / document ready &#8211; JS starts</li>
<li>Fully loaded and executed.</li></ul></li>
</ul>

<p>Test in several browsers, as load times will vary.
Test from several worldwide locations.
<a href="http://www.webpagetest.org">webpagetest.org</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/notes-from-oreillys-website-optimization/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Open up your WiFi</title>
		<link>http://www.darkcoding.net/software/open-up-your-wifi/</link>
		<comments>http://www.darkcoding.net/software/open-up-your-wifi/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 00:28:27 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Society]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[open]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[wifi]]></category>
		<category><![CDATA[wireless]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=748</guid>
		<description><![CDATA[A few months back, I took the password off my WiFi router, and opened it up to the world, with SSID yes_we_are_sharing. Why?

The best answers are given by security expert Bruce Shneier &#8211; why open wireless. The second best answer is that Tor hacker Jacob Applebaum also runs open WiFi.

Here are my answers, and the [...]]]></description>
			<content:encoded><![CDATA[<p>A few months back, I took the password off my WiFi router, and opened it up to the world, with SSID <code>yes_we_are_sharing</code>. Why?</p>

<p>The best answers are given by security expert <a href="http://www.schneier.com/blog/archives/2008/01/my_open_wireles.html">Bruce Shneier &#8211; why open wireless</a>. The second best answer is that <a href="http://en.wikipedia.org/wiki/Tor_%28anonymity_network%29">Tor</a> hacker <a href="http://en.wikipedia.org/wiki/Jacob_Appelbaum">Jacob Applebaum</a> also runs open WiFi.</p>

<p>Here are my answers, and the reasons why you should join us.</p>

<p><span id="more-748"></span></p>

<h2>What&#8217;s changed?</h2>

<p>The only difference between an encrypted wireless network and an open wireless network is that the part between your computer and the router is no longer encrypted by the router. Anyone can listen in, and anyone can connect to your router and access the big wide Internet.</p>

<p>The part between your computer and your router is only a tiny part of the journey your data takes between you and, say, your online bank. None of the part after the router has changed; if it was encrypted before, it still is, and if it wasn&#8217;t is still isn&#8217;t, and you should fix that. Read one for how.</p>

<h2>Reason: It&#8217;s nice</h2>

<p>Have you ever used anyone else&#8217;s open router? Maybe one named &#8216;dlink&#8217;, &#8216;linksys&#8217;, or a Mac AirPort? I bet you were happy that was there for you. When you move into a new home, when you have problems with your connection, or simply when you&#8217;re out and about, it&#8217;s great to have a free network. Opening up my network is my small way of giving back.</p>

<h2>Myth: Bad people in your front garden</h2>

<p>The first question most people ask about open wifi is this: What if someone uses my connection to do something bad?</p>

<p>To use your connection, they would have to be a direct neighbour of yours, or sitting outside your house. </p>

<p>Isn&#8217;t it more likely they would go that extra block to the warm and dry coffee shop?</p>

<p>Do you hear of coffee shop owners going to jail much? What about the &#8216;dlink&#8217; and &#8216;linksys&#8217; people? &#8220;Bad People&#8221; typically have their own Internet connection.</p>

<h2>Real Risk: Eavesdropping</h2>

<p>You might be worried that other people will steal your secrets. Again, they&#8217;d have to be sitting in your front garden. </p>

<p>There&#8217;s a much better place to steal people&#8217;s online banking data, than your front garden: in a coffee shop, at a conference, or, best of all, in an airport. </p>

<h2>Reason: It makes you safer</h2>

<p>You should setup your machine so that <em>all</em> your connections are encrypted, wherever you are. Opening your home wireless gives you that extra discipline.</p>

<ul>
<li><p>Secure your web browsing. When doing anything senstive, make sure you are using the &#8216;https&#8217; protocol (your browser will be showing a padlock).</p></li>
<li><p>Secure your email. If using webmail, make sure it&#8217;s over https. GMail, to Google&#8217;s great credit, has that as the default. If you are using regular email, makes sure you use the encrypted protocol &#8211; IMAPS or POPS.</p></li>
<li><p>Secure you IM conversations. <a href="http://en.wikipedia.org/wiki/Google_Talk#Encryption">Google Talk is encrypted</a> if your client supports it (<a href="http://www.pidgin.im/">Pidgin</a> does). <a href="http://forum.skype.com/index.php?showtopic=18543">Skype is encrypted</a>. As far as I could tell, Yahoo Messenger isn&#8217;t, so avoid it.</p></li>
</ul>

<h2>Weak Risk: Your neighbourhood hacker</h2>

<p>If you&#8217;re using Windows, you should be running a personal firewall on your machine. I believe there is now one built-in to Windows. Make sure it is switched on.</p>

<h2>Administration</h2>

<p>Most computers will auto-connect to any available wireless network when they start up. If you notice the same machines on your network every day for a while, they are probably auto-connecting. You&#8217;ll need to add rules to your router to ban their MAC address, shunting them back to their own router.</p>

<p>The point of open wireless isn&#8217;t to make your neighbours Internet connection redundant, but to temporarily help people out. Your neighbours don&#8217;t want to use your connection, as their is typically faster for them (they are closer to their router).</p>

<p>Happy sharing!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/open-up-your-wifi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Setting up Monit on Ubuntu</title>
		<link>http://www.darkcoding.net/software/setting-up-monit-on-ubuntu/</link>
		<comments>http://www.darkcoding.net/software/setting-up-monit-on-ubuntu/#comments</comments>
		<pubDate>Sun, 28 Feb 2010 23:18:23 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[monit]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[sysadmin]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=737</guid>
		<description><![CDATA[Monit tells you if something goes wrong on your server, and tries to fix it. It can, for example, alert you:


When a process dies.
When a machine stops responding to network requests
When your machine has too high load average, memory consumption, or CPU usage.
When a file changes, hasn&#8217;t changed for a period of time, or grows [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://mmonit.com/monit/">Monit</a> tells you if something goes wrong on your server, and tries to fix it. It can, for example, alert you:</p>

<ul>
<li>When a process dies.</li>
<li>When a machine stops responding to network requests</li>
<li>When your machine has too high load average, memory consumption, or CPU usage.</li>
<li>When a file changes, hasn&#8217;t changed for a period of time, or grows beyond a certain size.</li>
</ul>

<p>It can run a script of your choosing to attempt to fix the problem. It has an <a href="http://mmonit.com/monit/screenshots/monit.html">HTTP interface</a> that shows you essential stats about the services you are monitoring. For detailed graphs, I recommend <a href="http://www.darkcoding.net/software/setting-up-munin-on-ubuntu/">Munin</a>.</p>

<p>Here&#8217;s how to get it working on Ubuntu:</p>

<p><span id="more-737"></span></p>

<h2>Editing the config file</h2>

<div class="quickcodenoclick"><code><br />
sudo apt-get install monit<br />
sudo vim /etc/default/monit<br />
</code></div>

<p>Edit the single line to <code>startup=1</code>. </p>

<p>The config file that comes with monit is well commented, but just in case here&#8217;s the breakdown.</p>

<div class="quickcodenoclick"><code><br />
sudo vim /etc/monit/monitrc<br />
</code></div>

<p>Set Monit to check services every two minutes (120 seconds), and log to <code>/var/log/daemon.log</code></p>

<div class="quickcodenoclick"><code><br />
set daemon 120<br />
set logfile syslog facility log_daemon<br />
</code></div>

<p>Setup email alerts:</p>

<div class="quickcodenoclick"><code><br />
set mailserver localhost<br />
set mail-format { from: monit@myserver.domain.com }<br />
set alert sysadmin@domain.com<br />
</code></div>

<p>Switch on the HTTP interface, allow access from anywhere, and require a username and password. Make it a decent password, because the HTTP interface allows you to stop and start services.</p>

<div class="quickcodenoclick"><code><br />
set httpd port 2812&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;use address myserver.domain.com&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;allow 0.0.0.0/0.0.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;allow myusername:mypassword<br />
</code></div>

<p>Monitor the machine itself:</p>

<div class="quickcodenoclick"><code><br />
check system myserver.domain.com<br />
&nbsp;&nbsp;&nbsp;&nbsp;if loadavg (1min) &gt; 4 then alert<br />
&nbsp;&nbsp;&nbsp;&nbsp;if loadavg (5min) &gt; 3 then alert<br />
&nbsp;&nbsp;&nbsp;&nbsp;if memory usage &gt; 75% then alert<br />
&nbsp;&nbsp;&nbsp;&nbsp;if cpu usage (user) &gt; 70% then alert<br />
&nbsp;&nbsp;&nbsp;&nbsp;if cpu usage (system) &gt; 30% then alert<br />
&nbsp;&nbsp;&nbsp;&nbsp;if cpu usage (wait) &gt; 20% then alert<br />
</code></div>

<p>Then monitor all the services running on that box.</p>

<p>If you monitor the <code>totalcpu</code> resource, note that is a percentage of <em>all</em> CPUs. On a 4 CPU machine, 25% represents a process consuming 100% of one CPU.</p>

<p>For the Apache monitor, the PID file is defined in <code>/etc/apache2/envvars</code>, and is usually <code>/var/run/apache2.pid</code>.</p>

<p>Here are the service monitoring lines from my config:</p>

<p><a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode7377');">Quick Code</a></p></p>

<div id="quickcode7377" class="quickcode"><code><br />
check process apache with pidfile /var/run/apache2.pid<br />
&nbsp;&nbsp;&nbsp;&nbsp;start program = &quot;/etc/init.d/apache2 start&quot; with timeout 20 seconds<br />
&nbsp;&nbsp;&nbsp;&nbsp;stop program&nbsp;&nbsp;= &quot;/etc/init.d/apache2 stop&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;if totalcpu &gt; 20% for 2 cycles then alert<br />
&nbsp;&nbsp;&nbsp;&nbsp;if totalcpu &gt; 20% for 5 cycles then restart<br />
&nbsp;<br />
check process nginx with pidfile /var/run/nginx.pid<br />
&nbsp;&nbsp;&nbsp;&nbsp;start program = &quot;/etc/init.d/nginx start&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;stop program = &quot;/etc/init.d/nginx stop&quot;<br />
&nbsp;<br />
check process gearmand with pidfile /var/run/gearman/gearmand.pid<br />
&nbsp;&nbsp;&nbsp;&nbsp;start program = &quot;/etc/init.d/gearman-job-server start&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;stop program = &quot;/etc/init.d/gearman-job-server stop&quot;<br />
&nbsp;<br />
check process memcached with pidfile /var/run/memcached.pid<br />
&nbsp;&nbsp;&nbsp;&nbsp;start program = &quot;/etc/init.d/memcached start&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;stop program = &quot;/etc/init.d/memcached stop&quot;<br />
&nbsp;<br />
check process mysqld with pidfile /var/run/mysqld/mysqld.pid<br />
&nbsp;&nbsp;&nbsp;&nbsp;start program = &quot;/etc/init.d/mysql start&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;stop program = &quot;/etc/init.d/mysql stop&quot;<br />
</code></div>

<p>Start monit, and query it:</p>

<div class="quickcodenoclick"><code><br />
sudo /etc/init.d/monit start<br />
sudo monit status<br />
</code></div>

<p>You need the HTTP interface to use the &#8217;status&#8217; command.</p>

<h2>Monitoring MySQL replication</h2>

<p>To monit MySQL replication, create a script to touch a file if replication is still running. Put that script in cron. Get monit to check that file. </p>

<p>The idea comes from <a href="http://www.elevatedrails.com/articles/2007/12/05/replication-monitoring-with-monit/">replication monitoring with monit</a>, where they use Ruby. </p>

<p>I ported the script to Python, as a Django management command.</p>

<p>The crontab:</p>

<div class="quickcodenoclick"><code><br />
# m h&nbsp;&nbsp;dom mon dow&nbsp;&nbsp; command<br />
* * * * * /usr/local/myproject/mysql-watchdog-cron.sh<br />
</code></div>

<p>The shell script:</p>

<div class="quickcodenoclick"><code><br />
#!/bin/bash<br />
cd /usr/local/myproject<br />
/usr/bin/python manage.py mysql_replication_monit &gt;&gt; /dev/null 2&gt;&amp;1<br />
</code></div>

<p>The Django command:</p>

<p><a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode73711');">Quick Code</a></p></p>

<div id="quickcode73711" class="quickcode"><code><br />
import os<br />
import logging<br />
&nbsp;<br />
from django.core.management.base import NoArgsCommand<br />
from django.db import connection<br />
&nbsp;<br />
WATCH = &#039;/usr/local/myproject/mysql_monit_watchdog&#039;<br />
&nbsp;<br />
def mysql_fetch_one_dict(cursor):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&quot;Like DB-API&#039;s fetch_one but returns a dict instead of a tuple&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;data = cursor.fetchone()<br />
&nbsp;&nbsp;&nbsp;&nbsp;if not data:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return None<br />
&nbsp;&nbsp;&nbsp;&nbsp;desc = cursor.description<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;dict = {}<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (name, value) in zip(desc, data):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dict[name[0]] = value<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;return dict<br />
&nbsp;<br />
class Command(NoArgsCommand):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&#039;Touch a file if MySQL replication is running&#039;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;help = &#039;Touch a file if MySQL replication is running. Call from cron. Monit checks that file&#039;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;def handle_noargs(self, **options):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;Called by NoArgsCommand&#039;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cursor = connection.cursor()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cursor.execute(&#039;SHOW SLAVE STATUS&#039;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;row = mysql_fetch_one_dict(cursor)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if row[&#039;Slave_IO_Running&#039;] == &#039;Yes&#039; and row[&#039;Slave_SQL_Running&#039;] == &#039;Yes&#039;:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with file(WATCH, &#039;a&#039;):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;os.utime(WATCH, None)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logging.error(&#039;*ERROR*: Slave IO not running&#039;)<br />
</code></div>

<p>Add these lines to /etc/monit/monitrc:</p>

<div class="quickcodenoclick"><code><br />
check file mysql_replication with path /usr/local/myproject/mysql_monit_watchdog&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;if timestamp &gt; 3 minutes then alert&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</code></div>

<p>Happy monitoring!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/setting-up-monit-on-ubuntu/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Setting up Munin on Ubuntu</title>
		<link>http://www.darkcoding.net/software/setting-up-munin-on-ubuntu/</link>
		<comments>http://www.darkcoding.net/software/setting-up-munin-on-ubuntu/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 05:50:14 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[graph]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[munin]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=723</guid>
		<description><![CDATA[Munin is a system monitoring tool. It produces graphs for Apache, MySQL, Nginx, CPU, Memory, Disk, etc. Example munin installation &#8211; Live.

Here are my notes from setting it up, they are brief, but should help you get going.

All the monitored machines run a small daemon called munin-node. One machine is the  central server. Every [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://munin.projects.linpro.no/">Munin</a> is a system monitoring tool. It produces graphs for Apache, MySQL, Nginx, CPU, Memory, Disk, etc. <a href="http://munin.ping.uio.no/ping.uio.no/knuth.ping.uio.no.html">Example munin installation &#8211; Live</a>.</p>

<p>Here are my notes from setting it up, they are brief, but should help you get going.</p>

<p>All the monitored machines run a small daemon called <code>munin-node</code>. One machine is the  central server. Every few minutes it gathers data from all the nodes (including itself), generates the graphs, and writes out some HTML files.</p>

<p><span id="more-723"></span></p>

<p>In my case I have a proxy running <a href="http://wiki.nginx.org/Main">Nginx</a> and acting as the Munin central server, and two nodes each with <a href="http://en.wikipedia.org/wiki/Apache_HTTP_Server">Apache</a> and <a href="http://en.wikipedia.org/wiki/MySQL">MySQL</a>.</p>

<p>Everything that Munin monitors is in a separate plugin. Most of the ones you&#8217;ll need are bundled, but might need switching on.</p>

<p>If you only have one machine, that is your only node. Do both steps on that machine.</p>

<h2>On each node</h2>

<div class="quickcodenoclick"><code><br />
sudo apt-get install munin-node munin-plugins-extra<br />
</code></div>

<p>Edit <code>/etc/munin/munin-node.conf</code> and add these lines:</p>

<div class="quickcodenoclick"><code><br />
host_name mynode.mydomain.com&nbsp;&nbsp;# Hostname of the node machine<br />
allow 192.168.122.201&nbsp;&nbsp; # IP address of the central server<br />
host 192.168.122.203&nbsp;&nbsp;&nbsp;&nbsp;# Host IP address<br />
</code></div>

<p>If your Apache is not on port 80, edit the plugin config file <code>/etc/munin/plugin-conf.d/munin-node</code> and change or add these lines:</p>

<div class="quickcodenoclick"><code><br />
[apache_*]<br />
env.url&nbsp;&nbsp; http://127.0.0.1:%d/server-status?auto<br />
env.ports 81<br />
</code></div>

<p>Link the Apache plugin (it may already be linked, no problem)</p>

<div class="quickcodenoclick"><code><br />
cd /etc/munin/plugins/<br />
sudo ln -s /usr/share/munin/plugins/apache_processes apache_processes<br />
</code></div>

<p>Switch on Apache ExtendedStatus</p>

<div class="quickcodenoclick"><code><br />
sudo vi /etc/apache2/mods-available/status.conf<br />
</code></div>

<p>Outside of the <code>Location</code> section, add <code>ExtendedStatus On</code>
<a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode7236');">Quick Code</a></p></p>

<div id="quickcode7236" class="quickcode"><code><br />
&lt;IfModule mod_status.c&gt;<br />
&nbsp;<br />
ExtendedStatus On<br />
&lt;Location /server-status&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;SetHandler server-status<br />
&nbsp;&nbsp;&nbsp;&nbsp;Order deny,allow<br />
&nbsp;&nbsp;&nbsp;&nbsp;Deny from all<br />
&nbsp;&nbsp;&nbsp;&nbsp;Allow from localhost ip6-localhost<br />
&lt;/Location&gt;<br />
&nbsp;<br />
&lt;/IfModule&gt;<br />
</code></div>

<p>Install the Perl www packages (the Munin Apache plug needs them):</p>

<div class="quickcodenoclick"><code><br />
sudo apt-get install libwww-perl<br />
</code></div>

<p>Bounce munin-node and apache</p>

<div class="quickcodenoclick"><code><br />
sudo /etc/init.d/munin-node restart<br />
sudo /etc/init.d/apache2 reload<br />
</code></div>

<h2>On the central server</h2>

<div class="quickcodenoclick"><code><br />
sudo apt-get install munin munin-node munin-plugins-extra<br />
</code></div>

<p>Setup a crontab to run the data gathering task every five minutes</p>

<div class="quickcodenoclick"><code><br />
sudo -u munin crontab -e<br />
</code></div>

<p>Add this line</p>

<div class="quickcodenoclick"><code><br />
*/5 * * * *&nbsp;&nbsp;&nbsp;&nbsp; /usr/bin/munin-cron<br />
</code></div>

<p>Edit <code>/etc/munin/munin.conf</code> and set the nodes you want to gather data from, and their IP addresses.</p>

<div class="quickcodenoclick"><code><br />
[central.mydomain.com]<br />
&nbsp;&nbsp;&nbsp;&nbsp;address 127.0.0.1<br />
&nbsp;&nbsp;&nbsp;&nbsp;use_node_name yes<br />
&nbsp;<br />
[node1.mydomain.com]<br />
&nbsp;&nbsp;&nbsp;&nbsp;address 192.168.122.203<br />
&nbsp;&nbsp;&nbsp;&nbsp;use_node_name yes<br />
&nbsp;<br />
[node2.mydomain.com]<br />
&nbsp;&nbsp;&nbsp;&nbsp;address 192.168.122.202<br />
&nbsp;&nbsp;&nbsp;&nbsp;use_node_name yes<br />
</code></div>

<p>Run it!</p>

<div class="quickcodenoclick"><code><br />
sudo -u munin munin-cron<br />
</code></div>

<p>Look in <code>/var/www/</code>. You should see a <code>munin</code> directory.</p>

<p>Setup your web server to point to the HTML files. If like me you&#8217;re using Nginx, edit <code>/etc/nginx/sites-available/mydomain.com</code>, and add these lines</p>

<div class="quickcodenoclick"><code><br />
location /munin/ {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;root /var/www/;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expires off;<br />
&nbsp;&nbsp;auth_basic &quot;Munin&quot;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;auth_basic_user_file /etc/nginx/.htpasswd;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</code></div>

<p>Create the htpasswd file, or see <a href="http://snippets.aktagon.com/snippets/109-Password-protecting-a-folder-resource-with-Nginx">password protecting a folder with Nginx</a>.</p>

<div class="quickcodenoclick"><code><br />
htpasswd -c /etc/nginx/.htpasswd username<br />
</code></div>

<p>Try it out, by browsing to http://myserver.com/munin. <em>myserver.com</em> is of course the address your Nginx is serving.</p>

<p>You should see some graphs, but they will be blank, or display Nan instead of numbers. Be patient. In 10 &#8211; 15 minutes Nan will be replaced by numbers, and the graphs will slowly start appearing.</p>

<h2>Add nginx monitoring</h2>

<p>A bit of fancy footwork required here. This might of improved by the time you read this.</p>

<div class="quickcodenoclick"><code><br />
sudo apt-get install libwww-perl<br />
</code></div>

<p>Download Munin plugins <a href="http://munin.projects.linpro.no/browser/trunk/plugins/node.d/nginx_request.in?rev=2167">nginx request</a> and <a href="http://munin.projects.linpro.no/browser/trunk/plugins/node.d/nginx_status.in?rev=2167">nginx status</a>.</p>

<p>Replace the very first line of each one, the one that says <code>@@PERL@@</code> with this line</p>

<div class="quickcodenoclick"><code><br />
#!/usr/bin/perl -w<br />
</code></div>

<p>Comment out the rest of the header. Open the file in vim, hit Esc, and type</p>

<div class="quickcodenoclick"><code><br />
:3,56s/^/#/&nbsp;&nbsp;&lt;Enter&gt;<br />
:wq &lt;Enter&gt;<br />
</code></div>

<p><em>Note: I&#8217;m obviously missing an install step here. If you know, please help me in the comments</em></p>

<p>Copy the plugins into munin&#8217;s available plugins directory</p>

<div class="quickcodenoclick"><code><br />
cp nginx_request /usr/share/munin/plugins<br />
cp nginx_status /usr/share/munin/plugins<br />
sudo chmod ug+x /usr/share/munin/plugins/nginx_*<br />
</code></div>

<p>Edit your nginx config to switch on status. Edit <code>/etc/nginx/sites-available/mydomain.com</code> and add these lines:</p>

<div class="quickcodenoclick"><code><br />
location /nginx_status {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stub_status on;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_log&nbsp;&nbsp; off;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#allow 127.0.0.1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#deny all;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;allow all;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</code></div>

<p>Restart nginx: <code>sudo /etc/init.d/nginx restart</code></p>

<p>Link the plugins into the active plugins directory</p>

<div class="quickcodenoclick"><code><br />
cd /etc/munin/plugins/<br />
perl nginx_request autoconf<br />
&nbsp;<br />
sudo ln -s /usr/share/munin/plugins/nginx_request nginx_request<br />
sudo ln -s /usr/share/munin/plugins/nginx_status nginx_status<br />
</code></div>

<p>And you should be set. Happy monitoring!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/setting-up-munin-on-ubuntu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Restarting MySQL master-master replication</title>
		<link>http://www.darkcoding.net/software/restarting-mysql-master-master-replication/</link>
		<comments>http://www.darkcoding.net/software/restarting-mysql-master-master-replication/#comments</comments>
		<pubDate>Sun, 14 Feb 2010 20:24:13 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[replication]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=699</guid>
		<description><![CDATA[If your MySQL (5.0+) replication is broken, there&#8217;s two ways to fix it: The easy way, and the right way.

Run commands starting with $ on Unix. Run commands starting with mysql> in the MySQL client.

The easy way: Skip the problem

If you hit both databases at the same time, with the same INSERT, they will create [...]]]></description>
			<content:encoded><![CDATA[<p>If your MySQL (5.0+) replication is broken, there&#8217;s two ways to fix it: The easy way, and the right way.</p>

<p><em>Run commands starting with <strong>$</strong> on Unix. Run commands starting with <strong>mysql></strong> in the MySQL client.</em></p>

<h2>The easy way: Skip the problem</h2>

<p>If you hit both databases at the same time, with the same INSERT, they will create their own record, and try and replicate to the other, which already has that record, causing a duplicate error. </p>

<p>In a simple case like that, you just want to skip the offending statement:</p>

<div class="quickcodenoclick"><code><br />
mysql&gt;SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;<br />
</code></div>

<p>More details on <a href="http://www.ducea.com/2008/02/13/mysql-skip-duplicate-replication-errors/">skipping MySQL duplicate errors</a></p>

<p>Most of the time, you skip one statement, and replication breaks again straight away, because there&#8217;s a whole queue of problem statements coming up. </p>

<h2>The right way: Rebuild</h2>

<p>If you are not sure that you can skip the duplicate, or if replication has been broken long enough that your two servers are out of synch, pick one database to be the master, and rebuild the other from a copy of that master.</p>

<p><span id="more-699"></span></p>

<p>First make sure your site is only using the master server. Stop any processes that might modify data on the server you need to rebuild.</p>

<p>We have two database servers:</p>

<ul>
<li>Good Server: The good one, with the correct data. </li>
<li>Rebuilding Server: The one we are fixing. All it&#8217;s data will be erased with the Good Server data.</li>
</ul>

<p>You&#8217;ll need a unix command line and SQL command line on both servers. If you don&#8217;t already use it, after you&#8217;ve got this fixed, I recommend checking out <a href="http://en.wikipedia.org/wiki/GNU_Screen">Screen</a>.</p>

<h3>1. On the Good Server</h3>

<p>Dump data from the Good Server. The <code>master-data</code> switch adds a statement at the end of the file to start replication. </p>

<p>The <code>quick</code> switch makes dumping large tables use a lot less memory, so on a <a href="http://linode.com">VPS</a> it&#8217;s much faster.</p>

<p>All tables will locked during the dump.</p>

<p>Replace &#8216;my_database&#8217; with your database name.</p>

<div class="quickcodenoclick"><code><br />
$ mysqldump &#45;-add-drop-table &#45;-master-data <br />
&#45;-quick -u root -p my_database &gt; my_database.sql<br />
$ bzip2 my_database.sql<br />
</code></div>

<h3>2. On the Rebuilding Server</h3>

<p>Copy the dump onto the Rebuilding Server. Replace &#8216;myuser&#8217;, &#8216;good-server&#8217; and &#8216;my_database&#8217; as appropriate.</p>

<div class="quickcodenoclick"><code><br />
$ scp myuser@good-server:my_database.sql.bz2 .<br />
$ bunzip my_database.sql<br />
</code></div>

<p>Load the dump. This can take a few minutes for a large database.</p>

<div class="quickcodenoclick"><code><br />
mysql&gt; stop slave;<br />
$ mysql -u root -p my_database &lt; my_database.sql <br />
mysql&gt; show slave status\G<br />
</code></div>

<p>You should see <code>Slave_IO_Running: Yes</code> and <code>Slave_SQL_Running: Yes</code>. The <code>master-data</code> switch to <code>mysqldump</code>, in step 1, started replication at the right place for us. How nice. I love MySQL.</p>

<p>The \G means show vertical instead of the usual horizontal. It works with any MySQL command.</p>

<p>Now you have statements flowing Good Server &#8211;> Rebuilding Server. Next we need to get data going the other way.</p>

<div class="quickcodenoclick"><code><br />
mysql&gt; flush tables with read lock;<br />
mysql&gt; show master status;<br />
</code></div>

<p>Make a note of the File and Position rows.</p>

<h3>3. On the Good Server</h3>

<p>Set the slave here to be in synch with Rebuilding Server. Use the file name and log position from the previous step.</p>

<div class="quickcodenoclick"><code><br />
mysql&gt; stop slave;<br />
mysql&gt; change master to master_log_file=&#039;mysql-bin.000044&#039;, <br />
master_log_pos=132059667;<br />
mysql&gt; start slave;<br />
mysql&gt; show slave status\G<br />
</code></div>

<h3>4. On the Rebuilding Server</h3>

<div class="quickcodenoclick"><code><br />
mysql&gt; unlock tables;<br />
</code></div>

<p>Start the processes that point to the Rebuilding Server&#8217;s database. Done!</p>

<h2>Monitor replication</h2>

<p>The database is done, but you&#8217;re not done yet. </p>

<p>If you don&#8217;t already have good monitoring, take 15 minutes to setup <a href="http://mmonit.com/monit/">Monit</a> on all your production servers. </p>

<p>Monit is easy to setup (a word away from the monster that is Nagios), and will save again and again.</p>

<p>Once you have Monit working, take another 15 mins to <a href="http://www.elevatedrails.com/articles/2007/12/05/replication-monitoring-with-monit/">monitor MySQL replication with Monit</a>.</p>

<p><em>Now</em> you&#8217;re done :-)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/restarting-mysql-master-master-replication/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Memcached: List all keys</title>
		<link>http://www.darkcoding.net/software/memcached-list-all-keys/</link>
		<comments>http://www.darkcoding.net/software/memcached-list-all-keys/#comments</comments>
		<pubDate>Tue, 20 Oct 2009 16:50:30 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[memcached cache keys list]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=596</guid>
		<description><![CDATA[	In the general case, there is no way to list all the keys that a memcached instance is storing. You can, however, list something like the first 1Meg of keys, which is usually enough during development. Here&#8217;s how:

	Telnet to your server:

	telnet 127.0.0.1 11211

	

	List the items, to get the slab ids:

	stats items
STAT items:3:number 1
STAT items:3:age 498
STAT [...]]]></description>
			<content:encoded><![CDATA[	<p>In the general case, there is <a href="http://code.google.com/p/memcached/wiki/FAQ#Can_I_iterate_the_items_of_the_memcached_server?">no way to list all the keys</a> that a <a href="http://www.danga.com/memcached/">memcached</a> instance is storing. You can, however, list something like the first 1Meg of keys, which is usually enough during development. Here&#8217;s how:</p>

	<p>Telnet to your server:</p>

	<p><blockquote>telnet 127.0.0.1 11211</blockquote></p>

	<p><span id="more-596"></span></p>

	<p>List the items, to get the slab ids:</p>

	<p><blockquote>stats items<br />
<span class="caps">STAT</span> items:3:number 1<br />
<span class="caps">STAT</span> items:3:age 498<br />
<span class="caps">STAT</span> items:22:number 1<br />
<span class="caps">STAT</span> items:22:age 498<br />
<span class="caps">END</span></blockquote></p>

	<p>The first number after &#8216;items&#8217; is the slab id. Request a cache dump for each slab id, with a limit for the max number of keys to dump:</p>

	<p><blockquote>stats cachedump 3 100<br />
<span class="caps">ITEM</span> views.decorators.cache.cache_header..cc7d9 [6 b; 1256056128 s]<br />
<span class="caps">END</span><br />
stats cachedump 22 100<br />
<span class="caps">ITEM</span> views.decorators.cache.cache_page..8427e [7736 b; 1256056128 s]<br />
<span class="caps">END</span></blockquote></p>

	<p>Thanks to Boris Partensky in the Memcached group <a href="http://groups.google.com/group/memcached/browse_thread/thread/632ce89cff47522d?pli=1">here</a></p>

	<p>There you go!</p>

 ]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/memcached-list-all-keys/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Choosing a message queue for Python on Ubuntu on a VPS</title>
		<link>http://www.darkcoding.net/software/choosing-a-message-queue-for-python-on-ubuntu-on-a-vps/</link>
		<comments>http://www.darkcoding.net/software/choosing-a-message-queue-for-python-on-ubuntu-on-a-vps/#comments</comments>
		<pubDate>Mon, 10 Aug 2009 05:05:13 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[gearman]]></category>
		<category><![CDATA[messaging]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[queue]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=539</guid>
		<description><![CDATA[More and more, my web apps need to run things in the background: Sending email, re-calculating values, fetching website thumbnails, etc. In short, I need a message queue in my toolbox.

Luckily for me, message queues are this years Hot New Thing, so there&#8217;s some good options. I looked at RabbitMQ, Gearman, Beanstalkd and StompServer.


I&#8217;d like [...]]]></description>
			<content:encoded><![CDATA[<p>More and more, my web apps need to run things in the background: Sending email, re-calculating values, fetching website thumbnails, etc. In short, I need a message queue in my toolbox.</p>

<p>Luckily for me, message queues are this years <a href="http://simonwillison.net/tags/messagequeues/">Hot New Thing</a>, so there&#8217;s some good options. I looked at <a href="http://www.rabbitmq.com/">RabbitMQ</a>, <a href="http://gearman.org">Gearman</a>, <a href="http://xph.us/software/beanstalkd/">Beanstalkd</a> and <a href="http://stompserver.rubyforge.org/">StompServer</a>.</p>

<p><span id="more-539"></span>
I&#8217;d like the message queue to play nice with Python, with Ubuntu, and take almost no memory, as I&#8217;m on a <a href="http://linode.com">Virtual Private Server</a>, and I&#8217;d like it to stay up forever. I want small and solid.</p>

<h2>Summary</h2>

<table border="1" cellpadding="2">
<tr>
  <td></td>
  <th>RabbitMQ</th>
  <th>Gearman</th>
  <th>Beanstalkd</th>
  <th>StompServer</th>
</tr>

<tr>
 <th>Language</th>
 <td>Erlang</td>
 <td>C</td>
 <td>C</td>
 <td>Ruby</td>
</tr>

<tr>
 <th>In Ubuntu?</th>
 <td>Yes: rabbitmq-server</td>
 <td>Yes: gearman-job-server</td>
 <td>No</td>
 <td>No, it&#8217;s a Ruby gem</td>
</tr>

<tr>
 <th>Python lib</th>
 <td>amqplib</td>
 <td>gearman</td>
 <td>pybeanstalk</td>
 <td>stomp-py</td>
</tr>

<tr>
 <th>In PyPI?</th>
 <td>Yes</td>
 <td>Yes</td>
 <td>No</td>
 <td>No</td>
</tr>

<tr>
  <th>Memory</th>
  <td>9Mb</td>
  <td>1.4Mb</td>
  <td>0.5Mb</td>
  <td>7Mb</td>
</tr>

<tr>
 <th>Protocol</th>
 <td>AMQP</td>
 <td>Custom</td>
 <td>Custom</td>
 <td>STOMP</td>
</tr>

<tr>
 <th>License</th>
 <td>MPL</td>
 <td>BSD</td>
 <td>GPL</td>
 <td>MIT</td>
</tr>

</table>

<p>Memory size is the resident set size, obtained like so: <code>ps -Ao pid,rsz,args | grep &lt;name&gt;</code>. If there is a better way of estimating memory please let me know in the comments.</p>

<h2>RabbitMQ</h2>

<p>An all-singing all-dancing &#8220;complete and highly reliable Enterprise Messaging system&#8221;. With language like that you&#8217;d expect horrible bloat and per-cpu licensing, but happily that&#8217;s not the case. It&#8217;s straightforward to setup and relatively lean.</p>

<p>The protocol, <a href="http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol">AMQP</a>, comes from the financial world, and is intended to replace Tibco&#8217;s RendezVous, the backbone of most investment banks. There&#8217;s lots of documentation, lots of users, a healthy ecosystem, and it looks good on your CV.<br />
I tried RabbitMQ first, and liked it so much I almost stopped my evaluation right there and deployed it.</p>

<p>The best tutorial for using it from Python is here: <a href="http://blogs.digitar.com/jjww/2009/01/rabbits-and-warrens/">Rabbits and Warrens</a></p>

<h4>Publisher</h4>

<p><a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode5391');">Quick Code</a></p></p>

<div id="quickcode5391" class="quickcode"><code><br />
import sys<br />
import time<br />
&nbsp;<br />
from amqplib import client_0_8 as amqp<br />
&nbsp;<br />
conn = amqp.Connection(host=&quot;localhost:5672&quot;, userid=&quot;guest&quot;, password=&quot;guest&quot;, virtual_host=&quot;/&quot;, insist=False)<br />
chan = conn.channel()<br />
&nbsp;<br />
i = 0<br />
while 1:<br />
&nbsp;&nbsp;&nbsp;&nbsp;msg = amqp.Message(&#039;Message %d&#039; % i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;msg.properties[&quot;delivery_mode&quot;] = 2<br />
&nbsp;&nbsp;&nbsp;&nbsp;chan.basic_publish(msg,exchange=&quot;sorting_room&quot;,routing_key=&quot;testke y&quot;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;i += 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;time.sleep(1)<br />
&nbsp;<br />
chan.close()<br />
conn.close()<br />
</code></div>

<h4>Consumer</h4>

<p><a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode5392');">Quick Code</a></p></p>

<div id="quickcode5392" class="quickcode"><code><br />
from amqplib import client_0_8 as amqp<br />
&nbsp;<br />
conn = amqp.Connection(host=&quot;localhost:5672&quot;, userid=&quot;guest&quot;, password=&quot;guest&quot;, virtual_host=&quot;/&quot;, insist=False)<br />
chan = conn.channel()<br />
&nbsp;<br />
chan.queue_declare(queue=&quot;po_box&quot;, durable=True, exclusive=False, auto_delete=False)<br />
chan.exchange_declare(exchange=&quot;sorting_room&quot;, type=&quot;direct&quot;, durable=True, auto_delete=False,)<br />
&nbsp;<br />
chan.queue_bind(queue=&quot;po_box&quot;, exchange=&quot;sorting_room&quot;, routing_key=&quot;testkey&quot;)<br />
&nbsp;<br />
def recv_callback(msg):<br />
&nbsp;&nbsp;&nbsp;&nbsp;print msg.body<br />
&nbsp;<br />
chan.basic_consume(queue=&#039;po_box&#039;, no_ack=True, callback=recv_callback, consumer_tag=&quot;testtag&quot;)<br />
&nbsp;<br />
while True:<br />
&nbsp;&nbsp;&nbsp;&nbsp;chan.wait()<br />
&nbsp;<br />
#chan.basic_cancel(&quot;testtag&quot;)<br />
#chan.close()<br />
#conn.close()<br />
</code></div>

<h2>Gearman</h2>

<p>Gearman is a system to farm out work to other machines, dispatching function calls to machines that are better suited to do work, to do work in parallel, to load balance lots of function calls, or to call functions between languages.</p>

<p>Developed by <a href="http://www.danga.com/">Danga Interactive</a> (essentially Brad Fitzpatrick, who brought us Memcached and Perlbal). Used by LiveJournal, Digg and Yahoo.</p>

<p><em>Ubuntu users:</em> Make sure you install package <code>gearman-job-server</code>, which is the newer leaner C version of Gearman. Don&#8217;t install gearman-server, that is the old Perl version. Also install package <code>gearman-tools</code> to get the command line tool.</p>

<h4>Client</h4>

<div class="quickcodenoclick"><code><br />
import sys<br />
import time<br />
&nbsp;<br />
from gearman import GearmanClient, Task<br />
&nbsp;<br />
client = GearmanClient([&quot;127.0.0.1&quot;])<br />
&nbsp;<br />
i = 0<br />
while 1:<br />
&nbsp;&nbsp;&nbsp;&nbsp;client.dispatch_background_task(&#039;speak&#039;, i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;print &#039;Dispatched %d&#039; % i<br />
&nbsp;&nbsp;&nbsp;&nbsp;i += 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;time.sleep(1)<br />
</code></div>

<h4>Worker</h4>

<div class="quickcodenoclick"><code><br />
import time<br />
&nbsp;<br />
from gearman import GearmanWorker<br />
&nbsp;<br />
def speak(job):<br />
&nbsp;&nbsp;&nbsp;&nbsp;r = &#039;Hello %s&#039; % job.arg<br />
&nbsp;&nbsp;&nbsp;&nbsp;print r<br />
&nbsp;&nbsp;&nbsp;&nbsp;return r<br />
&nbsp;<br />
worker = GearmanWorker(&quot;[127.0.0.1]&quot;)<br />
worker.register_function(&#039;speak&#039;, speak, timeout=3)<br />
worker.work()<br />
</code></div>

<h2>Beanstalkd</h2>

<p>Beanstalkd is a fast, distributed, in-memory workqueue service. Its interface is generic, but was designed for use in reducing the latency of page views in high-volume web applications by running most time-consuming tasks asynchronously.</p>

<p>Developed for a very popular Facebook Application. The smallest memory footprint: after startup, connecting, sending a few messages, it&#8217;s resident memory size (rsz) was still only <strong>0.5 Mb</strong>!</p>

<p>To install the server:</p>

<ul>
<li>sudo apt-get install libevent-dev</li>
<li>wget http://xph.us/dist/beanstalkd/beanstalkd-1.3.tar.gz</li>
<li>tar xvzf beanstalkd-1.3.tar.gz</li>
<li>./configure</li>
<li>make (there&#8217;s no install step, it just generates the file &#8216;beanstalkd&#8217;)</li>
</ul>

<p>To install the Python library:</p>

<ul>
<li>wget http://pybeanstalk.googlecode.com/files/pybeanstalk-0.11.1.tar.gz</li>
<li>extract it</li>
<li>sudo python setup.py install</li>
</ul>

<p>There&#8217;s a good tutorial here: <a href="http://parand.com/say/index.php/2008/10/12/beanstalkd-python-basic-tutorial/">http://parand.com/say/index.php/2008/10/12/beanstalkd-python-basic-tut orial/</a></p>

<h4>Producer</h4>

<p><a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode5395');">Quick Code</a></p></p>

<div id="quickcode5395" class="quickcode"><code><br />
import time<br />
&nbsp;<br />
from beanstalk import serverconn<br />
from beanstalk import job<br />
&nbsp;<br />
def producer_main(connection):<br />
&nbsp;&nbsp;&nbsp;&nbsp;i = 0<br />
&nbsp;&nbsp;&nbsp;&nbsp;while True:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data = &#039;This is data to be consumed (%s)!&#039; % (i,)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print data<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data = job.Job(jid=i,data=data, conn=connection)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data.Queue()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time.sleep(1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i += 1;<br />
&nbsp;<br />
connection = serverconn.ServerConn(&#039;localhost&#039;, 11300)<br />
#connection.job = job.Job<br />
producer_main(connection)<br />
</code></div>

<h4>Consumer</h4>

<p><a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode5396');">Quick Code</a></p></p>

<div id="quickcode5396" class="quickcode"><code><br />
from beanstalk import serverconn<br />
from beanstalk import job<br />
&nbsp;<br />
def consumer_main(connection):<br />
&nbsp;&nbsp;&nbsp;&nbsp;while True:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;j = connection.reserve()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print &#039;got work: %s&#039; % j.data<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;j.Finish()<br />
&nbsp;<br />
connection = serverconn.ServerConn(&#039;localhost&#039;, 11300)<br />
connection.job = job.Job<br />
consumer_main(connection)<br />
</code></div>

<h2>StompServer</h2>

<p>StompServer is a lightweight pure Ruby <a href="http://en.wikipedia.org/wiki/Streaming_Text_Orientated_Messaging_Protocol">STOMP</a> server. </p>

<p>To install the server on Ubuntu:</p>

<ul>
<li>sudo apt-get install ruby-dev rubygems</li>
<li>sudo gem install stompserver</li>
</ul>

<p>To install the Python library:</p>

<ul>
<li>wget http://stomppy.googlecode.com/files/stomp.py-2.0.1.tar.gz</li>
<li>extract it</li>
<li>sudo python setup.py install</li>
</ul>

<p>There&#8217;s a good Python / Stompserver tutorial here: <a href="http://morethanseven.net/2008/09/14/using-python-and-stompserver-get-started-message-q/">http://morethanseven.net/2008/09/14/using-python-and-stompserver-get-s tarted-message-q/</a></p>

<h4>Sender</h4>

<p><a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode5397');">Quick Code</a></p></p>

<div id="quickcode5397" class="quickcode"><code><br />
import time<br />
&nbsp;<br />
import stomp<br />
&nbsp;<br />
conn = stomp.Connection()<br />
conn.start()<br />
conn.connect()<br />
&nbsp;<br />
i = 0<br />
while 1:<br />
&nbsp;&nbsp;&nbsp;&nbsp;conn.send(&#039;Message %d&#039; % i, destination=&#039;/queue/test&#039;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;i += 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;time.sleep(1)<br />
&nbsp;<br />
conn.disconnect()<br />
</code></div>

<h4>Listener</h4>

<p><a class="quickcode" title="Code" href="javascript:toggleLayer('quickcode5398');">Quick Code</a></p></p>

<div id="quickcode5398" class="quickcode"><code><br />
import time<br />
import sys<br />
&nbsp;<br />
import stomp<br />
&nbsp;<br />
class MyListener(object):<br />
&nbsp;&nbsp;&nbsp;&nbsp;def on_error(self, headers, message):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print &#039;received an error %s&#039; % message<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;def on_message(self, headers, message):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print &#039;received a message %s&#039; % message<br />
&nbsp;<br />
conn = stomp.Connection()<br />
conn.set_listener(&#039;&#039;, MyListener())<br />
conn.start()<br />
conn.connect()<br />
&nbsp;<br />
conn.subscribe(destination=&#039;/queue/test&#039;, ack=&#039;auto&#039;)<br />
&nbsp;<br />
while 1:<br />
&nbsp;&nbsp;&nbsp;&nbsp;time.sleep(2)<br />
</code></div>

<h2>Results and Conclusions</h2>

<p>I&#8217;d be happy working with any of these four. All four were easy to setup, fast, decent in memory consumption, and had good Python libraries.</p>

<p>RabbitMQ has the most mindshare (it is the only one which registers on <a href="http://www.google.com/trends?q=rabbitmq%2C+gearman%2C+beanstalkd%2C+stompserver&#038;ctab=0&#038;geo=all&#038;date=all&#038;sort=0">Google Trends</a>), but it took the most memory and is the most complex to use. It looks like a great product, but it&#8217;s Message Oriented Middleware, not an in-memory job queue, so it&#8217;s not what I&#8217;m looking for.</p>

<p>StompServer had the least documentation, and took several times more memory than Gearman and Beanstalkd. In seems the most immature project, but would probably be a good choice for someone working in Ruby.</p>

<p>Beanstalkd is great. I would like to see it in the Ubuntu repositories, and it&#8217;s Python lib in PyPI, but aside from that, I can&#8217;t fault it. I&#8217;m not choosing it, because Gearman is even better.</p>

<p>Gearman was designed for exactly the problem I have, takes almost no memory (1.4Mb), has a great pedigree (Danga), is widely deployed (LiveJournal, Digg, Yahoo), is in Ubuntu, has a Python library in PyPI, and someone helped me out on the #gearman IRC channel straight away. It even has queue persistence and clustering. So, <a href="http://twitter.com/jacobian/status/2761378698">Gearman it is</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/choosing-a-message-queue-for-python-on-ubuntu-on-a-vps/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Django dynamic forms and formsets</title>
		<link>http://www.darkcoding.net/software/django-dynamic-forms-and-formsets/</link>
		<comments>http://www.darkcoding.net/software/django-dynamic-forms-and-formsets/#comments</comments>
		<pubDate>Tue, 16 Jun 2009 01:08:23 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[django form formset]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=517</guid>
		<description><![CDATA[	A couple of great posts which explain Django dynamic forms and advanced formset usage very clearly:

	
	James Bennett: So you want a dynamic formMalcolm Tredinnick: Advanced Formset usage in Django


 ]]></description>
			<content:encoded><![CDATA[	<p>A couple of great posts which explain <a href="http://www.djangoproject.com">Django</a> dynamic forms and advanced formset usage very clearly:</p>

	<p><ul></p>
	<p><li>James Bennett: <a href="http://www.b-list.org/weblog/2008/nov/09/dynamic-forms/">So you want a dynamic form</a></li><li>Malcolm Tredinnick: <a href="http://www.pointy-stick.com/blog/2009/01/23/advanced-formset-usage-django/">Advanced Formset usage in Django</a></li><br />
</ul></p>

 ]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/django-dynamic-forms-and-formsets/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How and Why to extend Firefox in Javascript</title>
		<link>http://www.darkcoding.net/software/how-and-why-to-extend-firefox-in-javascript/</link>
		<comments>http://www.darkcoding.net/software/how-and-why-to-extend-firefox-in-javascript/#comments</comments>
		<pubDate>Thu, 11 Jun 2009 03:29:22 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[owv2009 firefox speaking vancouver extension addon javascript]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=512</guid>
		<description><![CDATA[	I will be giving this talk on Friday 12th June, at Open Web Vancouver 2009.

	View more Keynote presentations from Graham King.

 ]]></description>
			<content:encoded><![CDATA[	<p>I will be giving this talk on Friday 12th June, at <a href="http://www.openwebvancouver.ca/">Open Web Vancouver 2009</a>.</p>

	<p><div style="width:425px;text-align:left" id="__ss_1565242"><object style="margin:0px" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=owv2009-090610200628-phpapp02&#038;stripped_title=how-and-why-to-extend-firefox" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=owv2009-090610200628-phpapp02&#038;stripped_title=how-and-why-to-extend-firefox" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object><div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;">View more <a style="text-decoration:underline;" href="http://www.slideshare.net/">Keynote presentations</a> from <a style="text-decoration:underline;" href="http://www.slideshare.net/graham_king">Graham King</a>.</div></div></p>

 ]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/how-and-why-to-extend-firefox-in-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Unix shared directory permissions: GUID and umask</title>
		<link>http://www.darkcoding.net/software/unix-shared-directory-permissions-guid-and-umask/</link>
		<comments>http://www.darkcoding.net/software/unix-shared-directory-permissions-guid-and-umask/#comments</comments>
		<pubDate>Wed, 10 Jun 2009 19:11:26 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[mercurial guid unix group permission directory]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=502</guid>
		<description><![CDATA[I setup my Mercurial repository in the same way we used to do CVS, then SVN: A directory owned by a group, with the GUID bit, and all users who need to commit are in that group. 

The steps are, create the group and add relevant users to it:


&#160;&#160;&#160;&#160;sudo groupadd topsecretgroup
&#160;&#160;&#160;&#160;sudo usermod -a -G topsecretgroup [...]]]></description>
			<content:encoded><![CDATA[<p>I setup my Mercurial repository in the same way we used to do CVS, then SVN: A directory owned by a group, with the GUID bit, and all users who need to commit are in that group. </p>

<p>The steps are, create the group and add relevant users to it:</p>

<div class="quickcodenoclick"><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;sudo groupadd topsecretgroup<br />
&nbsp;&nbsp;&nbsp;&nbsp;sudo usermod -a -G topsecretgroup graham</code></div>

<p><span id="more-502"></span></p>

<p>Change the project directory to be owned by that group, and accessible by no-one else:</p>

<div class="quickcodenoclick"><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;cd topsecretproject/<br />
&nbsp;&nbsp;&nbsp;&nbsp;sudo chown graham:topsecretgroup -R .<br />
&nbsp;&nbsp;&nbsp;&nbsp;sudo chmod g=u,o= -R .&nbsp;&nbsp;</code></div>

<p>Set the GUID bit on all the directories, so that new files and directories are created owned by the group:</p>

<div class="quickcodenoclick"><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;find . -type d | sudo xargs chmod g+s</code></div>

<p>Change the umask for everyone, so that new files are created with read and write permissions for the group:</p>

<div class="quickcodenoclick"><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;sudo vi /etc/profile<br />
&nbsp;&nbsp;&nbsp;&nbsp;change &#039;umask 022&#039; to &#039;umask 002&#039; </code></div>

<p>The last part, changing the umask, isn&#8217;t ideal. It works on Debian and Ubuntu, because every user has their own group. I would rather a more focused solution, just for that directory &#8211; suggestions welcome.</p>

<p>References:</p>

<p><a href="http://www.freehackers.org/thomas/2008/09/16/about-mercurial-and-permissions/">Mercurial and permissions</a><br />
<a href="http://www.selenic.com/mercurial/wiki/MultipleCommitters">Multiple Committers</a><br />
<a href="http://ubuntuforums.org/showthread.php?t=127084">Change Ubuntu global umask</a><br />
<a href="http://hgbook.red-bean.com/read/collaborating-with-other-people.html">Collaboration models</a>  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/unix-shared-directory-permissions-guid-and-umask/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Speaking at Open Web Vancouver 2009 in June</title>
		<link>http://www.darkcoding.net/software/speaking-open-web-vancouver-2009/</link>
		<comments>http://www.darkcoding.net/software/speaking-open-web-vancouver-2009/#comments</comments>
		<pubDate>Sun, 17 May 2009 21:30:59 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Future of Web Apps]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[owv09 speaking vancouver]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=455</guid>
		<description><![CDATA[I will be speaking at Open Web Vancouver on Thursday, June 11, 2009 and Friday, June 12, 2009.
That&#8217;s in Vancouver, B.C., Canada. There&#8217;s a very interesting speaker lineup, and the whole conference is reasonably priced, so come along, learn, interact, and enjoy Vancouver in the summertime.

My talk will be entitled How and Why to Extend [...]]]></description>
			<content:encoded><![CDATA[<p>I will be speaking at <a href="http://www.openwebvancouver.ca">Open Web Vancouver</a> on Thursday, June 11, 2009 and Friday, June 12, 2009.<br />
That&#8217;s in Vancouver, B.C., Canada. There&#8217;s a very interesting <a href="http://www.openwebvancouver.ca/speakers_sessions">speaker lineup</a>, and the whole conference is reasonably priced, so come along, learn, interact, and enjoy Vancouver in the summertime.</p>

<p>My talk will be entitled <strong>How and Why to Extend Firefox in Javascript (and Thunderbird, Komodo, and Songbird)</strong>. I will post the slides here in June.</p>

<p>See you there!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/speaking-open-web-vancouver-2009/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Micro-Zooids: A story</title>
		<link>http://www.darkcoding.net/software/micro-zooids-a-story/</link>
		<comments>http://www.darkcoding.net/software/micro-zooids-a-story/#comments</comments>
		<pubDate>Sun, 10 May 2009 22:23:13 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Ideas]]></category>
		<category><![CDATA[Misc]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Strategy]]></category>
		<category><![CDATA[story game youth]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=450</guid>
		<description><![CDATA[When I was 16, I wrote a computer game, called Micro Zooides. It was called that partly because on Windows .EXE files all start with the two characters MZ, and partly because it was about small creatures. Micro-Zooides was going to be about humanity&#8217;s progress, it was going to be Civilization, which didn&#8217;t exist yet.

The [...]]]></description>
			<content:encoded><![CDATA[<p>When I was 16, I wrote a computer game, called Micro Zooides. It was called that partly because on Windows .EXE files all start with the two characters <code>MZ</code>, and partly because it was about small creatures. Micro-Zooides was going to be about humanity&#8217;s progress, it was going to be <a href="http://www.civilization.com/">Civilization</a>, which didn&#8217;t exist yet.</p>

<p>The game had a splash screen of a Far Side comic, then a short video of me tromping through the woods like a Neanderthal, which my Dad filmed and which I digitized with a very early video capture card.</p>

<p>In <a href="http://en.wikipedia.org/wiki/Turbo_C%2B%2B#Historical_versions">Borland&#8217;s Turbo C++ 3.0</a> I wrote a basic graphics engine to display the tiles of the world, and an event loop so I could move the main character around the world. I drew sprites for a proto-human (the micro zooid), dirt, rocks and sticks. He could walk around the world, and pick up and put down rocks or sticks.</p>

<p>Then I took a break to plan. I have a proto-human, rocks, and sticks. How do I get to civilization?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/micro-zooids-a-story/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Turn on debug output in SVN</title>
		<link>http://www.darkcoding.net/software/turn-on-debug-output-in-svn/</link>
		<comments>http://www.darkcoding.net/software/turn-on-debug-output-in-svn/#comments</comments>
		<pubDate>Thu, 07 May 2009 23:02:38 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=435</guid>
		<description><![CDATA[After a desktop and server upgrade, my subversion client stopped working. I am using Digest authentication, and it kept asking me for the username and password. Wireshark showed me that the SVN client wasn&#8217;t sending the Authentication header. To find out more, I turned on Subversion&#8217;s debug output. Here&#8217;s how you do it:

Edit /etc/subversion/servers
Add this [...]]]></description>
			<content:encoded><![CDATA[<p>After a desktop and server upgrade, my subversion client stopped working. I am using Digest authentication, and it kept asking me for the username and password. <a href="http://www.wireshark.org/">Wireshark</a> showed me that the SVN client wasn&#8217;t sending the Authentication header. To find out more, I turned on Subversion&#8217;s debug output. Here&#8217;s how you do it:</p>

<p>Edit <strong>/etc/subversion/servers</strong><br />
Add this line at the end: <strong>neon-debug-mask = 511</strong></p>

<p>That showed me this error: <code>auth: '/' is inside auth domain: 0.</code></p>

<p>This means that the path I was requesting (the root of the repo) was not considered inside the <code>AuthDigestDomain</code> I had set in Apache.</p>

<p>It turns out that at some point in the upgrade of Apache, Subversion, or a library, the AuthDigestDomain requires a scheme. I had<br />
<code>AuthDigestDomain svn.myserver.com</code><br />
whereas it should of been<br />
<code>AuthDigestDomain http://svn.gkgk.org</code>.</p>

<p>So now you know.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/turn-on-debug-output-in-svn/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Foxden &#8211; your life on one page</title>
		<link>http://www.darkcoding.net/software/foxden-your-life-on-one-page/</link>
		<comments>http://www.darkcoding.net/software/foxden-your-life-on-one-page/#comments</comments>
		<pubDate>Tue, 07 Apr 2009 00:21:22 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=389</guid>
		<description><![CDATA[	Update: There is now a user group for Foxden: http://groups.google.com/group/foxden-users

	Foxden is a Firefox extension that allows you to tile all the web applications you use on one page. Imagine being able to see your email, your calendar, your bug tracker, feeds, twitter, whatever you use, on one page. Take a look at my setup:

	



	As you [...]]]></description>
			<content:encoded><![CDATA[	<p><strong>Update:</strong> There is now a user group for Foxden: <a href="http://groups.google.com/group/foxden-users">http://groups.google.com/group/foxden-users</a></p>

	<p><strong>Foxden is a Firefox extension that allows you to tile all the web applications you use on one page.</strong> Imagine being able to see your email, your calendar, your bug tracker, feeds, twitter, whatever you use, on one page. Take a look at my setup:</p>

	<p><a href="http://foxden.mozdev.org/Screenshot-Foxden.png" target="_blank"><br />
<img src="http://foxden.mozdev.org/Screenshot-Foxden-thumbnail.png" alt="Foxden screenshot" /><br />
</a></p>

	<p>As you can see, I have (counter clockwise from top left) my email, calendar, feed reader, todo list and a local text file for taking notes.</p>

	<p>It&#8217;s free, should work wherever Firefox 3 works, and it could be yours right now.</p>

	<p><a href="https://addons.mozilla.org/en-US/firefox/addon/11363">Download the Foxden Firefox extension</a></p>

 ]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/foxden-your-life-on-one-page/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Eddit: Reddit without the Rrrr!</title>
		<link>http://www.darkcoding.net/software/eddit-reddit-without-the-rrrr/</link>
		<comments>http://www.darkcoding.net/software/eddit-reddit-without-the-rrrr/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 16:06:32 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=215</guid>
		<description><![CDATA[Eddit is a bookmarklet for Reddit, which gives you more signal and less of the noise that makes you go Rrrr! Here&#8217;s how it improves reddit:



50 items on the page (reddit pages 1 and 2)
No next button at the bottom. You read then you go back to work.
Floating filter removing the things that make you [...]]]></description>
			<content:encoded><![CDATA[<p>Eddit is a bookmarklet for <a href="http://reddit.com">Reddit</a>, which gives you more signal and less of the noise that makes you go Rrrr! Here&#8217;s how it improves reddit:
</p>
<span id="more-215"></span>
<ul>
<li>50 items on the page (reddit pages 1 and 2)</li>
<li>No next button at the bottom. You read then you go back to work.</li>
<li>Floating filter removing the things that make you go Rrrrrr!
  <ul>
   	<li>pictures</li>
	<li>US politics</li>
	<li>swearing (curse)</li>
	<li>posts about reddit</li>
	<li>WTF, OMG.</li>
  </ul>
</li>
</ul>
<p>
Right click on the link below and bookmark it, then browse to <a href="http://reddit.com">reddit</a> and click the bookmark.
</p>
<p align="center">
<a href="javascript:(function(){s=document.createElement('script');s.setAttribute('src', 'http://darkcoding.net/eddit-min.js');document.body.appendChild(s);})();">Eddit: Reddit bookmarklet</a>
</p>
<p>Tested on Firefox 3, Opera 9 and IE 7.</p>
<p>
The idea is to reduce the noise and up the signal. The filters mean less noise, the 50 items mean more signal, and the lack of &#8216;next&#8217; button means less distraction.
</p>
<p>Eddit uses <a href="http://jquery.com">jQuery</a> which reddit already pulls in. The bookmarklet simply loads and runs a Javascript file from my server. If you want to modify it, here is the non-minified <a href="/eddit.js">source code of Eddit</a>. If you change it remember to edit your bookmarklet to load your new file.
</p>
<p>
<em>Historical note:</em> Eddit was originally built on <a href="http://appjet.com">AppJet</a> as a proxy, but that violated Reddit&#8217;s TOS, and it really belonged on the client anyway, so it&#8217;s a bookmarklet now.
</p>]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/eddit-reddit-without-the-rrrr/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Flex internals: Setting a button label</title>
		<link>http://www.darkcoding.net/software/flex-internals-setting-a-button-label/</link>
		<comments>http://www.darkcoding.net/software/flex-internals-setting-a-button-label/#comments</comments>
		<pubDate>Mon, 16 Mar 2009 23:50:32 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=306</guid>
		<description><![CDATA[Most ActionScript / Flash applications have a main event loop, triggered by Event.ENTER_FRAME. This is where the animation moves along to the next frame, or the sprites of the game are re-drawn in their new places.

In the Flex framework, you are expected to call invalidateDisplayList on the framework to say you need an update, and [...]]]></description>
			<content:encoded><![CDATA[<p>Most ActionScript / Flash applications have a main event loop, triggered by Event.ENTER_FRAME. This is where the animation moves along to the next frame, or the sprites of the game are re-drawn in their new places.</p>

<p>In the Flex framework, you are expected to call invalidateDisplayList on the framework to say you need an update, and actually do the update when the framework calls your updateDisplayList method. This is the invalidation / validation pattern.</p>

<p>I went searching in the Flex code to understand how this invalidation / validation step ties in with Flash&#8217;s event model. I ignored properties and sizing, and edited the code down to the bare essentials.</p>

<p>Here is what happens when you change the label of a button:</p>

<p><span id="more-306"></span></p>

<h4>1. Your code</h4>

<div class="quickcodenoclick"><code><br />
var myButton:Button = new Button();<br />
myButton.label = &quot;New Label&quot;;<br />
</code></div>

<h4>2. Button.as</h4>

<div class="quickcodenoclick"><code><br />
public function set label(value:String):void&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_label = value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;labelChanged = true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;invalidateDisplayList();<br />
}<br />
</code></div>

<p>This is what you are meant to do, if you build your own Flex component. You save the new value (in <code>_label</code>), record that it needs updating (<code>labelChanged = true</code>), and tell the framework that you are &#8216;dirty&#8217;, and hence need updating (<code>invalidateDisplayList</code>).</p>

<h4>3. UIComponent.as</h4>

<div class="quickcodenoclick"><code><br />
public function invalidateDisplayList():void {<br />
&nbsp;&nbsp;&nbsp;&nbsp;UIComponentGlobals.layoutManager.invalidateDisplayList(this);<br />
}<br />
</code></div>

<p><code>UIComponentGlobals.layoutManager</code> is a <code>mx.manager.LayoutManager</code></p>

<h4>4. LayoutManager.as</h4>

<div class="quickcodenoclick"><code><br />
public function invalidateDisplayList(obj:ILayoutManagerClient ):void&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;invalidateDisplayListFlag = true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callLaterObject.callLater(doPhasedInstantiation);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;invalidateDisplayListQueue.addObject(obj, obj.nestLevel);<br />
}<br />
</code></div>

<p>The last line adds our button to the list of objects that need validating. </p>

<p>The line before, <code>callLaterObject.callLater</code>, is where the action happens, so let&#8217;s follow it. <code>callLaterObject</code> is an <code>mx.core.UIComponent</code></p>

<h4>5. UIComponent.as</h4>

<div class="quickcodenoclick"><code><br />
public function callLater(method:Function, args:Array = null):void {<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;methodQueue.push(new MethodQueueElement(method, args));<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// sm is an mx.core.SystemManager<br />
&nbsp;&nbsp;&nbsp;&nbsp;sm.addEventListener(FlexEvent.RENDER, callLaterDispatcher);<br />
&nbsp;&nbsp;&nbsp;&nbsp;sm.addEventListener(FlexEvent.ENTER_FRAME, callLaterDispatcher);<br />
&nbsp;&nbsp;&nbsp;&nbsp;listeningForRender = true;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// Force a &quot;render&quot; event to happen soon<br />
&nbsp;&nbsp;&nbsp;&nbsp;sm.stage.invalidate();&nbsp;&nbsp;// stage is a flash.display.Stage<br />
}<br />
</code></div>

<p>Here we are, at the very heart of the framework. The <code>doPhasedInstantiation</code> method (parameter <code>method</code>) is added to the list of methods to call later. Then we listen for the <code>FlexEvent.RENDER</code> and <code>FlexEvent.ENTER_FRAME</code> events. This is starting to look familiar.</p>

<p><code>sm</code> is an instance of <code>mx.core.SystemManager</code>.</p>

<h3>6. SystemManager</h3>

<div class="quickcodenoclick"><code><br />
override public function addEventListener(type:String, listener:Function,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useCapture:Boolean = false,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;priority:int = 0,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;useWeakReference:Boolean = false):void<br />
 {<br />
&nbsp;&nbsp; if (type == FlexEvent.RENDER || type == FlexEvent.ENTER_FRAME)&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (type == FlexEvent.RENDER)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type = Event.RENDER;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type = Event.ENTER_FRAME;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stage.addEventListener(type, listener, useCapture, priority, useWeakReference);<br />
&nbsp;&nbsp; }<br />
}<br />
</code></div>

<p>This is it, the interface between the Flex framework and the Flash player. We listen to the familiar RENDER and ENTER_FRAME events.</p>

<h4>7. flash.display.Stage.invalidate</h4>

<p>As in step 5 <code>invalidate</code> was called on the stage, Flash will fire a RENDER event when we enter the next frame, cued by the ENTER_FRAME event.</p>

<p>A Flex application is only two frames (preloader on frame 1, application on frame 2), but once the second frame has been reached ENTER_FRAME will keep firing at the frame rate, which by default is 24 frames a second.</p>

<p>So about 1/24 th of a second after we call invalidate, RENDER gets fired, which takes us back down from Flash into the Flex framework.</p>

<h4>8. UIComponent.as</h4>

<div class="quickcodenoclick"><code><br />
private function callLaterDispatcher(event:Event):void&nbsp;&nbsp;{<br />
&nbsp;&nbsp; sm.removeEventListener(FlexEvent.RENDER, callLaterDispatcher);<br />
&nbsp;&nbsp; sm.removeEventListener(FlexEvent.ENTER_FRAME, callLaterDispatcher);<br />
&nbsp;&nbsp; for (var i:int = 0; i &lt; n; i++)&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var mqe:MethodQueueElement = MethodQueueElement(queue[i]);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mqe.method.apply(null, mqe.args);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</code></div>

<p>This calls all the <code>callLater</code> methods, including LayoutManager&#8217;s <code>doPhasedInstantiation</code>, which we added to that queue in step 5.</p>

<p>Note that we stop listening to the ENTER_FRAME events. As far as I can tell, there is no main loop in Flex, it&#8217;s all event driven. If nothing is happening in your application, then usually nothing is happening in the Flex framework.</p>

<h4>9. LayoutManager.as</h4>

<div class="quickcodenoclick"><code><br />
private function doPhasedInstantiation():void {<br />
&nbsp;&nbsp; if (invalidateDisplayListFlag) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;validateDisplayList();<br />
&nbsp;&nbsp; }<br />
}<br />
&nbsp;<br />
private function validateDisplayList():void {<br />
&nbsp;&nbsp; var obj:ILayoutManagerClient = ILayoutManagerClient(invalidateDisplayListQueue.removeSmallest());<br />
&nbsp;&nbsp; while (obj) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.validateDisplayList();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj = ILayoutManagerClient(invalidateDisplayListQueue.removeSmallest());<br />
&nbsp;&nbsp; }<br />
}<br />
</code></div>

<p>Iterate through all the objects that need updating, and call their <code>validateDisplayList</code> method. In step 4, our button was put on <code>invalidateDisplayListQueue</code>.</p>

<h4>10. UIComponent.as</h4>

<div class="quickcodenoclick"><code><br />
public function validateDisplayList():void&nbsp;&nbsp;{<br />
&nbsp;&nbsp; if (invalidateDisplayListFlag) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var unscaledWidth:Number = scaleX == 0 ? 0 : width / scaleX;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var unscaledHeight:Number = scaleY == 0 ? 0 : height / scaleY;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateDisplayList(unscaledWidth,unscaledHeight);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;invalidateDisplayListFlag = false;<br />
&nbsp;&nbsp; }<br />
}<br />
</code></div>

<p>The button class doesn&#8217;t override <code>validateDisplayList</code>, so it&#8217;s parent gets the call. It in turn calls <code>updateDisplayList</code>, which is what we have been waiting for.</p>

<h4>11. Button.as</h4>

<div class="quickcodenoclick"><code><br />
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {<br />
&nbsp;&nbsp; super.updateDisplayList(unscaledWidth, unscaledHeight);<br />
&nbsp;&nbsp; if (labelChanged)&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textField.text = label;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;labelChanged = false;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
}<br />
</code></div>

<p>And here we are, our <code>updateDisplayList</code> finally gets called. <code>textField</code> is a Flex specific subclass of <code>flash.text.TextField</code>, and is what actually gets rendered by the flash player. </p>

<h4>12. The actual displaying</h4>

<p>Flash completes the RENDER event, and renders the button to the screen, with it&#8217;s new label. And there you are, a new label on your button. How nice.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/flex-internals-setting-a-button-label/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Choosing the best Flex book &#8211; Flex book reviews</title>
		<link>http://www.darkcoding.net/software/choosing-the-best-flex-book-flex-book-reviews/</link>
		<comments>http://www.darkcoding.net/software/choosing-the-best-flex-book-flex-book-reviews/#comments</comments>
		<pubDate>Sun, 15 Mar 2009 22:05:29 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=287</guid>
		<description><![CDATA[UPDATE March 19th: Added First Steps in Flex, and Learning Flex 3.

You&#8217;re an experienced server-side programmer, with a background in C/C++/Java/C# or Python, but no Flash experience. You want to learn Flex. Which book should you buy to learn Flex 3? 
I have speed-read the following, so that you don&#8217;t have to:



Good Flex Books

0. Getting [...]]]></description>
			<content:encoded><![CDATA[<p>UPDATE March 19th: Added First Steps in Flex, and Learning Flex 3.</p>

<p>You&#8217;re an experienced server-side programmer, with a background in C/C++/Java/C# or Python, but no Flash experience. You want to learn Flex. Which book should you buy to learn Flex 3? 
I have speed-read the following, so that you don&#8217;t have to:</p>

<p><span id="more-287"></span></p>

<h2>Good Flex Books</h2>

<h4>0. <a href="http://www.senocular.com/flash/tutorials/as3withmxmlc/">Getting started with ActionScript3 with mxmlc</a></h4>

<p>A free online tutorial, which gets you writing your first ActionScript class, compiling it, and running the resulting SWF in flash player.</p>

<h4>1.<a href="http://www.amazon.com/gp/product/1590598156?ie=UTF8&#038;tag=darkcoding-20&#038;linkCode=as2&#038;camp=1789&#038;creative=9325&#038;creativeASIN=1590598156"> Foundation AS 3.0 w/ Flash CS3 and Flex</a> &#8211; Steve Webber &#8211; Friends of ED</h4>

<p>Flex is an application framework built on top for Flash, so you need to understand Flash to learn Flex. This book gets you building ActionScript apps. It explains some parts in Flash Authoring, but usually presents a pure ActionScript alternative, which only requires a text editor and mxmlc from the Flex SDK.</p>

<p>Learn about the Flash events, frames, graphics, etc. There&#8217;s a small bit of Flex at the end, but this will give you a solid foundation. And if, like me, you&#8217;re from a server-side background, playing an MP3 and graphing it in real time will be a lot of fun!</p>

<h4>2a. <a href="http://www.amazon.com/gp/product/0470277920?ie=UTF8&#038;tag=darkcoding-20&#038;linkCode=xm2&#038;camp=1789&#038;creativeASIN=0470277920">Adobe Flex 3.0 For Dummies</a> &#8211; Doug McCune</h4>

<p>Don&#8217;t let the title put you off, it&#8217;s a very good introduction to Flex, and one of only two books that explains the component lifecycle, and the invalidation pattern. Will get you up to speed fast. Most of what isn&#8217;t covered here is in the online Flex API.</p>

<h4>2b. <a href="http://www.amazon.com/gp/product/0596516215?ie=UTF8&#038;tag=darkcoding-20&#038;linkCode=xm2&#038;camp=1789&#038;creativeASIN=0596516215">Programming Flex 3</a> &#8211; Chafic Kazoun &#8211; O&#8217;Reilly</h4>

<p>Good coverage of all of Flex, including explaining what&#8217;s Flex and what&#8217;s Flash, and explaining the component lifecycle. More complete than the For Dummies. Up to the usual high O&#8217;Reilly standard. If you&#8217;re an experienced programmer, and you know how Flash works, get this one.</p>

<h4>3. <a href="http://www.firststepsinflex.com/">First Steps in Flex</a> &#8211; Bruce Eckel and James Ward &#8211; MindView</h4>

<p>Very short (140 pages) and very tight tour of Flex (with a peppering of OO basics). Comes out of the Flex classes the authors have taught. As the name implies, it&#8217;s a great first exposure book, which will get you working in Flex fast.</p>

<p>It&#8217;s too short to cover the component lifecycle, how Flash and Flex relate, or how to work without the GUI (with mxmlc). Almost all the examples are in MXML, when in practice you&#8217;ll mostly write ActionScript classes. </p>

<p>If you&#8217;re short of time, a manager, or you&#8217;re learning Flex because you have to, this is the book for you. If you have more time and interest, prefer Programming Flex 3.</p>

<h4>4. <a href="http://www.amazon.com/gp/product/0596529856?ie=UTF8&#038;tag=darkcoding-20&#038;linkCode=xm2&#038;camp=1789&#038;creativeASIN=0596529856">Flex 3 Cookbook</a> &#8211; Various authors &#8211; O&#8217;Reilly</h4>

<p>Lots of helpful stuff in here. Not a good book to learn from, but a good second Flex book.</p>

<h2>Average Flex Books</h2>

<h4>Essential Guide to Flex 3 &#8211; Charles E. Brown &#8211; Friends of ED</h4>

<p>Very basic. It would get you going, but only just. It&#8217;s a fair title, which delivers what it promises &#8211; just the essentials. Takes 600 pages to cover what First Steps in Flex does in 140. Prefer First Steps in Flex or Flex 3 for Dummies.</p>

<h4>Flex 3 in Action &#8211; Tariq Ahmed &#8211; Manning</h4>

<p>A disappointing book from Manning, which usually does better. This would get you going, as it covers all the Flex basics, but doesn&#8217;t go any further. No coverage of the invalidation pattern or component lifecycle, no mention of Sprite at all, and a poor index. A plus is that it includes a voucher for a free PDF version of Flex 4 in Action, when it comes out. Prefer Programming Flex 3, the O&#8217;Reilly book.</p>

<h2>Poor Flex Books</h2>

<h4>Creating Mashups with Flex and AIR &#8211; Chris Korhonen &#8211; Friends of ED</h4>

<p>Pick any two buzzwords, and publish. This book is all over the place, with lots of bits of content, but nothing substantial at all.</p>

<h4>Pro Flex on Spring &#8211; Chris Giametta &#8211; APress</h4>

<p>Here we go again, you got your two buzzwords, let&#8217;s print. This is a Flex 2 book. The first three chapters are hand waving about project setup (staffing, tools, installing, etc), then in the next chapter we&#8217;re overriding updateDisplayList, without any mention of how that fits into the overall framework.</p>

<h4>AdvancED Flex 3 &#8211; Shashank-Tiwari &#8211; Friends of ED</h4>

<p>This book is all about connecting Flex to other things, such as Java, PHP, Twitter, Salesforce, etc. But I wanted to learn Flex, not the Salesforce API! It also uses updateDisplayList without telling you what&#8217;s going on.</p>

<p>A plus point is that this is the only book I found that doesn&#8217;t toe the Adobe Consulting party line on Cairgorm, the &#8216;official&#8217; Flex MVC framework, inspired by Java&#8217;s Struts. He points out it&#8217;s design shortcomings, how Struts isn&#8217;t a good match for Flex, and offers two alternatives.</p>

<h4>Learning Flex 3 &#8211; Alaric Cole &#8211; O&#8217;Reilly</h4>

<p>Should really of been called &#8216;Using Design view in Flex Builder&#8217;, as that&#8217;s pretty much all it covers. It takes forever to get going (&#8217;Who uses Flex&#8217;, &#8216;How Flex relates to C&#8217;, etc), and then has helpful sidebars telling you how to add comments using only the mouse (Click the Source menu, select &#8216;Add block comment&#8217;). Doesn&#8217;t deserve to be an O&#8217;Reilly &#8216;Learning &#8230;&#8217; book, and I think Tim knows, because it didn&#8217;t get an animal for the cover.</p>

<p>On the plus side it is the most visually appealing book in the list. If you don&#8217;t read books without pretty pictures, and don&#8217;t own a keyboard, this might be the Flex book for you.</p>

<h2>Special Mention &#8211; An Unusual Flex book</h2>

<h4><a href="http://www.amazon.com/gp/product/1430215984?ie=UTF8&#038;tag=darkcoding-20&#038;linkCode=xm2&#038;camp=1789&#038;creativeASIN=1430215984">Flex 3 Component Solutions: Building Amazing Interfaces with Flex Components</a> &#8211; Jack Herrington &#8211; Friends of ED</h4>

<p>I really liked this book. It is essentially a catalog of third party Flex components, which, here, means graphical widgets. He covers the open source libraries, such as AS3CoreLib, FlexLib, and Degrafa, and the commercial ones such as as3components.com, and ILOG&#8217;s Elixir.</p>

<p>With First Steps in Flex, this is also the only good coverage of using Flex&#8217;s states, which the other books show, but don&#8217;t explain, and don&#8217;t use in a helpful fashion.</p>

<p>Finally there&#8217;s a great tip to use Google&#8217;s Sketchup 3D modeller to pre-render an animation, as an alternative to PaperVision3D.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/choosing-the-best-flex-book-flex-book-reviews/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Math Dodger: A Flash game</title>
		<link>http://www.darkcoding.net/software/math-dodger-a-flash-game/</link>
		<comments>http://www.darkcoding.net/software/math-dodger-a-flash-game/#comments</comments>
		<pubDate>Sun, 15 Mar 2009 01:26:28 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=264</guid>
		<description><![CDATA[Los Angeles is under attack, by trigonometric functions! OMG! Trigo-what? If I wanted to do maths, I&#8217;d go to San Francisco!! You, like, totally gotta save L.A man. Enter your name, then move your tank.


Use the left and right arrow keys to rotate, the forward and back arrow keys to move.
The barrel of the tank [...]]]></description>
			<content:encoded><![CDATA[<p>Los Angeles is under attack, by trigonometric functions! OMG! Trigo-what? If I wanted to do maths, I&#8217;d go to San Francisco!! You, like, totally gotta save L.A man. Enter your name, then move your tank.<br />
</p>
<p>
<b>Use the left and right arrow keys to rotate, the forward and back arrow keys to move.</b><br />
<b>The barrel of the tank is the little black line. That&#8217;s the front.</b>
</p>
<p>
No, your tank can&#8217;t fire. Avoid the mathematical blobs. YEAH! <br />
The longer you live, the more points you get. A score above 100 is, like, totally AWESOME! Good luck Bro.<br />
Let me know in the comments how much you score.
</p>

<object width="500" height="375">
    <param name="movie" value="/MathDodger.swf" />
    <embed src="/MathDodger.swf" width="500" height="375" type="application/x-shockwave-flash" />
</object>
<br />
<span id="more-264"></span>

<p>
This game is for Paul, who quit the comfortable life of a Brighton based software engineer, to go teach maths in Birmingham. Turns out there is a point to maths &#8211; it&#8217;s computer graphics.<br />
The L.A. theme is just because I had that picture in my collection (which I took from the Griffith Observatory), and I&#8217;m a little nostalgic about it.
</p>
<p>
<a href="/MathDodgerSource.tar.gz">Download the source code</a>. This code should be considered licensed under the GNU General Public Licence.
</p>
<p>
You&#8217;ll need the <a href="http://www.adobe.com/products/flex/flexdownloads/">free Flex SDK</a> to build it, and the <a href="http://www.adobe.com/support/flashplayer/downloads.html">standalone flashplayer</a> (or know how to embed Flash in HTML) to run it.
</p>
<p>
Once you have those, unzip the source into a directory, and run: <br/> 
<code>mxmlc MathDodger.as ; flashplayer MathDodger.swf</code>
</p>
<p>
If you are interested in ActionScript (not just for animators!), I suggest starting with <a href="http://www.senocular.com/flash/tutorials/as3withmxmlc/">the Senocular tutorial</a>.
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/math-dodger-a-flash-game/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Javascript objects: And what is this?</title>
		<link>http://www.darkcoding.net/software/javascript-objects-and-what-is-this/</link>
		<comments>http://www.darkcoding.net/software/javascript-objects-and-what-is-this/#comments</comments>
		<pubDate>Mon, 23 Feb 2009 02:06:41 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=192</guid>
		<description><![CDATA[When writing object-oriented Javascript, there are two occasions when you need to be careful that this is set correctly: In inner functions and in callbacks.

this in inner functions


If you are not in an object, this refers to the global window object. 
If you are in an object&#8217;s method, this refers to that object, 
except in [...]]]></description>
			<content:encoded><![CDATA[<p>When writing object-oriented Javascript, there are two occasions when you need to be careful that <em>this</em> is set correctly: In inner functions and in callbacks.</p>

<h2><em>this</em> in inner functions</h2>

<ol>
<li>If you are not in an object, <em>this</em> refers to the global window object. </li>
<li>If you are in an object&#8217;s method, <em>this</em> refers to that object, </li>
<li>except in an inner function, when <em>this</em> refers to the global window object again. </li>
</ol>

<p>Number 3 is what you need to watch for. It is considered a bug in Javascript. Here is an illustration of the three cases:</p>

<p><span id="more-192"></span></p>

<div class="quickcodenoclick"><code><br />
// You need Firebug to run this script, otherwise <br />
// replace console.log with alert.<br />
&nbsp;<br />
console.log(&#039;Globally: &#039;+ this);<br />
&nbsp;<br />
var MyObj = function() {&nbsp;&nbsp;// Constructor<br />
}<br />
MyObj.prototype = {<br />
&nbsp;&nbsp; test: <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function() {<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function inner() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;In an inner function: &quot;+ this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(&quot;In an object&#039;s method: &quot;+ this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inner();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;<br />
var m = new MyObj();<br />
m.test();<br />
</code></div>

<p>The Firebug console will display this:
<code><br />
&nbsp;&nbsp;&nbsp;&nbsp;Globally: [object Window]<br />
&nbsp;&nbsp;&nbsp;&nbsp;In an object&#039;s method: [object Object]<br />
&nbsp;&nbsp;&nbsp;&nbsp;In an inner function: [object Window]<br />
</code></p>

<p>To get inner functions to see your object, you need to copy <em>this</em> into a local variable. Thanks to closures inner functions can see all the local variables of the containing function.</p>

<div class="quickcodenoclick"><code><br />
var MyObj = function() {&nbsp;&nbsp;// Constructor<br />
}<br />
MyObj.prototype = {<br />
&nbsp;&nbsp; test: <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function() {<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function inner() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Using _this instead of this<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;In an inner function: &quot;+ _this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Save in local variable<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var _this = this;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(&quot;In an object&#039;s method: &quot;+ this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inner();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;<br />
var m = new MyObj();<br />
m.test();<br />
</code></div>

<p>This will display what you expect:
<code><br />
&nbsp;&nbsp;&nbsp;&nbsp;In an object&#039;s method: [object Object]<br />
&nbsp;&nbsp;&nbsp;&nbsp;In an inner function: [object Object]<br />
</code></p>

<h2><em>this</em> in object callbacks</h2>

<p>Object scope is not preserved when you pass one of your object&#8217;s methods as a callback. Example:</p>

<div class="quickcodenoclick"><code><br />
var MyObj = function() {<br />
}<br />
MyObj.prototype = {<br />
&nbsp;<br />
&nbsp;&nbsp; test:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(&#039;test: &#039;+ this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setTimeout(this.onEvent, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br />
&nbsp;<br />
&nbsp;&nbsp; onEvent:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(&#039;onEvent: &#039;+ this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
var m = new MyObj();<br />
m.test();<br />
</code></div>

<p>This prints:
<code><br />
&nbsp;&nbsp;&nbsp;&nbsp;test: [object Object]<br />
&nbsp;&nbsp;&nbsp;&nbsp;onEvent: [object Window]<br />
</code></p>

<p>This is the case because when you pass <em>this.onEvent</em> as a callback, you are passing a function, like you might pass a string or an integer. The <em>setTimeout</em> function doesn&#8217;t know that it belongs to an object.</p>

<p>The answer is again to use the closure (that&#8217;s often the answer in Javascript), and wrap your call in an inner function, remembering the previous point about inner functions.</p>

<div class="quickcodenoclick"><code><br />
var MyObj = function() {<br />
}<br />
MyObj.prototype = {<br />
&nbsp;<br />
&nbsp;&nbsp; test:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(&#039;test: &#039;+ this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // save locally<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var _this = this;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // wrap and call<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setTimeout(function() {_this.onEvent()}, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br />
&nbsp;<br />
&nbsp;&nbsp; onEvent:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(&#039;onEvent: &#039;+ this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
var m = new MyObj();<br />
m.test();<br />
</code></div>

<p>As expected, this prints:
<code><br />
&nbsp;&nbsp;&nbsp;&nbsp;test: [object Object]<br />
&nbsp;&nbsp;&nbsp;&nbsp;onEvent: [object Object]<br />
</code></p>

<p>We save <em>this</em> in a local variable, then pass an inner function as the callback. That function has no context, but it does have a copy of our object saved in it&#8217;s <em>_this</em> variable. It calls <em>onEvent</em> on our object.</p>

<h2>That&#8217;s enough work for now</h2>

<p>The post title is inspired by this clip:</p>

<p><object width="425px" height="360px" ><param name="allowFullScreen" value="true"/><param name="wmode" value="transparent"/><param name="movie" value="http://mediaservices.myspace.com/services/media/embed.aspx/m=3938950,t=1,mt=video,searchID=,primarycolor=,secondarycolor="/><embed src="http://mediaservices.myspace.com/services/media/embed.aspx/m=3938950,t=1,mt=video,searchID=,primarycolor=,secondarycolor=" width="425" height="360" allowFullScreen="true" type="application/x-shockwave-flash" wmode="transparent"/></object></p>

<p>Happy Javascripting!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/javascript-objects-and-what-is-this/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Easy object-oriented Javascript the Python way</title>
		<link>http://www.darkcoding.net/software/easy-object-oriented-javascript-the-python-way/</link>
		<comments>http://www.darkcoding.net/software/easy-object-oriented-javascript-the-python-way/#comments</comments>
		<pubDate>Thu, 22 Jan 2009 23:47:30 +0000</pubDate>
		<dc:creator>Graham King</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=151</guid>
		<description><![CDATA[Javascript is not an opinionated language. At it&#8217;s heart it is a hash map. You can layer pretty much any idiom you want on top of it. I&#8217;d like to make it look like Python, and it&#8217;s pretty easy to do. They both are dynamically typed, have functions as first class objects, and can treat [...]]]></description>
			<content:encoded><![CDATA[<p>Javascript is not an opinionated language. At it&#8217;s heart it is a hash map. You can layer pretty much any idiom you want on top of it. I&#8217;d like to make it look like Python, and it&#8217;s pretty easy to do. They both are dynamically typed, have functions as first class objects, and can treat most types as hash maps.</p>

<p><img src="http://farm3.static.flickr.com/2103/1797950238_b2efb5f9c4.jpg" alt="Let's observe this moose" title="" /></p>

<p>Let&#8217;s assume code for a whimsical Moose Observation Project, and translate it from Python to Javascript.</p>

<p><span id="more-151"></span></p>

<h2>The Python</h2>

<p>Here is mop.py:</p>

<div class="quickcodenoclick"><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;ANSWER = 42<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;class Moose:<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BULL = 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;COW = 2<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def __init__(self, sex):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.legs = 4<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.sex = sex<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def speak(self):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if self.sex == Moose.BULL:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print &quot;a heavy grunt-like noise that can be heard up to half a kilometer away&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elif self.sex == Moose.COW:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print &quot;a wail-like bawl&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print &quot;Sexless moose say &quot;+ self._default_sound()<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def _default_sound(self):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return str(ANSWER)<br />
</code></div>

<h2>The Javascript</h2>

<p>And here is mop.js:</p>

<div class="quickcodenoclick"><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;if (typeof MOP == &quot;undefined&quot; || !MOP) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var MOP = {};<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;MOP.ANSWER = 42;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;MOP.Moose = function(sex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.legs = 4;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.sex = sex<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;MOP.Moose.BULL = 1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;MOP.Moose.CALF = 2;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;MOP.Moose.prototype = {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;speak:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (this.sex == MOP.Moose.BULL) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;a heavy grunt-like noise that can be heard up to half a kilometer away&quot;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (this.sex == MOP.Moose.COW) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;a wail-like bawl&quot;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;Sexless moose say &quot;+ this._default_sound());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_default_sound:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return MOP.ANSWER;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</code></div>

<h2>Using it</h2>

<p>In Javascript:</p>

<div class="quickcodenoclick"><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;var bennyTheMoose = new MOP.Moose(MOP.Moose.BULL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;bennyTheMoose.speak();<br />
</code></div>

<h2>The details</h2>

<p>Let&#8217;s look at the interesting parts in more detail. Note that below when I say <em>property</em> I mean <em>attribute or method</em>.</p>

<h4>The module is just a namespace</h4>

<p>Because you don&#8217;t know what other scripts might be included on a page that uses your script, you need to use the smallest amount possible of the global namespace. Create an object, and put everything else you define in that object. Yahoo, for example, puts everything they write in the <code>YAHOO</code> object / namespace. Here our namespace is called <code>MOP</code>.</p>

<h4>new: The class is a function which returns an object</h4>

<p>The two parts of Javascript that make object oriented programming possible are the <code>new</code> operator applied to a function, and the <code>prototype</code> attribute of that function.</p>

<p>When a function call is preceded by the <code>new</code> operator, it gets given a new object called <code>this</code>, and it returns it. The <code>Moose</code> function is your constructor It is good practice to define your object&#8217;s data in there, same as it is in Python. It returns your new instance.</p>

<p>If you forget the <code>new</code> operator, you get no warning. The function is called as a function instead of as a constructor. <code>this</code> is bound to the global <code>window</code> object, and the function returns <code>undefined</code>. Your call is perfectly valid, yet it does something very different to what you wanted. A lot of Javascript is like that!</p>

<h3>prototype: Instances can have methods &#8211; but ignore that</h3>

<p>A significant difference between Javascript and most other languages is that in Javascript object <em>instances</em> can have properties. In most other languages, only the class has properties, and all instances share them. We&#8217;re trying to be Pythonic, so let&#8217;s not use this feature, but knowing about it helps to understand the <code>prototype</code> attribute.</p>

<p>When you call a method on an instance, if that method is not found, it will try it&#8217;s <code>prototype</code> attribute. The <code>prototype</code> attribute is shared amongst all instances constructed with the same constructor (because <code>prototype</code> is an attribute of the constructor function). If we put all our methods on the prototype, we get the same behavior as in a more traditional object-oriented language.</p>

<h3>Object literals keep things tidy</h3>

<p>I&#8217;m using an <a href="http://www.wait-till-i.com/2006/02/16/show-love-to-the-object-literal/">object literal</a> to list the methods of the <code>Moose</code> class. Here is the equivalent code without using an object literal:</p>

<div class="quickcodenoclick"><code><br />
&nbsp;&nbsp; MOP.Moose.prototype.speak = <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (this.sex == MOP.Moose.BULL) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;a heavy grunt-like noise that can be heard up to half a kilometer away&quot;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (this.sex == MOP.Moose.COW) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;a wail-like bawl&quot;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;Sexless moose say &quot;+ this._default_sound());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;MOP.Moose.prototype._default_sound = <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return MOP.ANSWER;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />
</code></div>

<p>I think an object literal wraps the functions up more neatly and saves some typing.</p>

<h3>Private methods</h3>

<p>Javascript, like Python, doesn&#8217;t give you anything built in to declare an access level &#8211; everything is public. Using closures, you can have private methods in Javascript &#8211; examples <a href="http://yuiblog.com/blog/2007/06/12/module-pattern/">here</a> and <a href="http://dev.opera.com/articles/view/the-seven-rules-of-unobtrusive-javascrip/">item 6 here</a> &#8211; but that&#8217;s not how Python does it.</p>

<p>In Python the convention is to prefix private methods with a single underscore. They can still be called, but you are warning the user that they are now on their own, all bets are off. Let&#8217;s simply carry that convention over to Javascript: <code>_default_sound</code> is a private method.</p>

<h2>Conclusion</h2>

<ul>
<li>Put everything in a namespace.</li>
<li>You class is a function. </li>
<li>That function is your constructor. You declare your attributes in that constructor.</li>
<li>You call that function with the <code>new</code> operator.</li>
<li>The methods of your class go on the <code>prototype</code> attribute of that function.</li>
<li>Use the underscore-prefix convention to mark a private method.</li>
</ul>

<p>Javascript and Python have a lot in common, and applying some of <a href="http://www.python.org/dev/peps/pep-0020/">Python&#8217;s Zen</a> to Javascript helps me impose some order on the chimera that is Javascript. I hope it helps you too.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/easy-object-oriented-javascript-the-python-way/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
