Kyle Fullerhttps://fuller.li/2025-04-08T00:00:00+01:00Building a Corsi–Rosenthal Box2025-04-08T00:00:00+01:002025-04-08T00:00:00+01:00Kyle Fullertag:fuller.li,2025-04-08:/posts/building-a-corsi-rosenthal-box/<p>A <a href="https://en.wikipedia.org/wiki/Corsi%E2%80%93Rosenthal_Box">Corsi-Rosenthal
box</a> is a DIY air
cleaner which outperforms commercially available options at a fraction of the
cost.</p>
<p><img alt="Corsi Rosenthal Box" src="https://fuller.li/images/building-a-corsi-rosenthal-box/corsi-rosenthal-box.jpeg"></p>
<p>Sourcing the parts to build a Corsi-Rosenthal box outside of North America has
some challenges. The ubiquitous box fan typically used is non-existent, and
standardised filters are not readily available.</p>
<p>After doing some research on potential options I had found <a href="https://xcancel.com/PlasticFull/status/1553014055186620417">Stefan Stojanovic's
research and testing on the
topic</a>, along with
<a href="https://drive.google.com/file/d/1PvfC3grgjeX22kR4F-M_W1UJ8OQlaw1L/view">build
guide</a>.
This looked promising, however I quickly discovered it was not possible to
source the fan used anymore, and I …</p><p>A <a href="https://en.wikipedia.org/wiki/Corsi%E2%80%93Rosenthal_Box">Corsi-Rosenthal
box</a> is a DIY air
cleaner which outperforms commercially available options at a fraction of the
cost.</p>
<p><img alt="Corsi Rosenthal Box" src="https://fuller.li/images/building-a-corsi-rosenthal-box/corsi-rosenthal-box.jpeg"></p>
<p>Sourcing the parts to build a Corsi-Rosenthal box outside of North America has
some challenges. The ubiquitous box fan typically used is non-existent, and
standardised filters are not readily available.</p>
<p>After doing some research on potential options I had found <a href="https://xcancel.com/PlasticFull/status/1553014055186620417">Stefan Stojanovic's
research and testing on the
topic</a>, along with
<a href="https://drive.google.com/file/d/1PvfC3grgjeX22kR4F-M_W1UJ8OQlaw1L/view">build
guide</a>.
This looked promising, however I quickly discovered it was not possible to
source the fan used anymore, and I was unable to find another viable options.</p>
<p>Eventually I stumbled across some variants that use PC fans, originally aimed
at reducing the power consumption of running the system. I discovered that <a href="https://www.texairfilters.com/comparing-the-performance-of-corsi-rosenthal-boxes-made-with-box-fans-and-pc-fans/">in
testing an array of PC fans has negligible performance difference than a box
fan in a
CR-box</a>.</p>
<p>Eventually, I was able to put together a parts list to be able to construct the
box with somewhat available parts found in the UK (and I imagine available in
many countries).</p>
<p><img alt="Parts" src="https://fuller.li/images/building-a-corsi-rosenthal-box/parts.jpeg"></p>
<table>
<thead>
<tr>
<th style="text-align: center;">#</th>
<th>Part</th>
<th style="text-align: right;">Price</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">6x</td>
<td>Arctic P14 Fans <sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></td>
<td style="text-align: right;">£41</td>
</tr>
<tr>
<td style="text-align: center;">6x</td>
<td>Generic 140mm fan guards</td>
<td style="text-align: right;">£12</td>
</tr>
<tr>
<td style="text-align: center;">1x</td>
<td>USB-C PD to 12v DC5521 Adapter <sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup> <sup id="fnref:3"><a class="footnote-ref" href="#fn:3">3</a></sup></td>
<td style="text-align: right;">£8</td>
</tr>
<tr>
<td style="text-align: center;">1x</td>
<td>12v DC5521 to 3/4-pin fan adapter</td>
<td style="text-align: right;">£7</td>
</tr>
<tr>
<td style="text-align: center;">1x</td>
<td>3/4-pin fan splitter (1 -> 6 fans)</td>
<td style="text-align: right;">£5</td>
</tr>
<tr>
<td style="text-align: center;">1x</td>
<td>Ikea TRETAKT Smart Plug <sup id="fnref:4"><a class="footnote-ref" href="#fn:4">4</a></sup></td>
<td style="text-align: right;">£6</td>
</tr>
<tr>
<td style="text-align: center;">4x</td>
<td>3M Filtrete MPR 1900 (20x25x1) <sup id="fnref:5"><a class="footnote-ref" href="#fn:5">5</a></sup> <sup id="fnref:6"><a class="footnote-ref" href="#fn:6">6</a></sup></td>
<td style="text-align: right;">£140</td>
</tr>
<tr>
<td style="text-align: center;"></td>
<td></td>
<td style="text-align: right;">£215</td>
</tr>
</tbody>
</table>
<p><img alt="Fan test run" src="https://fuller.li/images/building-a-corsi-rosenthal-box/fans.jpeg"></p>
<p>While constructing the lid which contains the fan, I had found that a utility
knife worked best to cut circular holes in cardboard. I used additional
cardboard layers glued together to reinforce the lid to that it is able to
hold the weight of 6 fans.</p>
<p><img alt="Lid with fans installed" src="https://fuller.li/images/building-a-corsi-rosenthal-box/lid.jpeg"></p>
<p>Overall, the CR-box meets expectations. I have previously built an <a href="https://www.airgradient.com/documentation/diy/">air quality
sensor</a> which I am able to see
that the CR-box is able to filter PM2.5 particulates effectively based on its
measurements.</p>
<p>The blue dotted line indicates when the CR-box was deployed, on the 25th. When
the box is operating the PM2.5's detected drop to less than 0.5 µg/m³, and
build up over time when the system is off.</p>
<p><img alt="PM2.5 metric data" src="https://fuller.li/images/building-a-corsi-rosenthal-box/pm25.png"></p>
<div class="footnote">
<hr>
<ol>
<li id="fn:1">
<p>Arctic fans are relatively affordable and performant, used in the
<a href="https://en.wikipedia.org/wiki/Corsi%E2%80%93Rosenthal_Box">testing by Jim
Rosenthal</a>. <a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:2">
<p>USB-C PD allows using generic and efficient AC adapters. USB-C PD AC
adapter and USB-C cable is not included in the parts list as I already had
these. <a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
<li id="fn:3">
<p>The selected fans operate at 12v at 0.15A each. Therefore the adapter
needs to be able to drive over 10.8w. <a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text">↩</a></p>
</li>
<li id="fn:4">
<p>A smart plug will allow automatically running the CR box either on a
schedule, or in reaction to the measured air quality. <a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text">↩</a></p>
</li>
<li id="fn:5">
<p>A MERV 13 filter appears to be more than sufficient for my use-case, if
it was available a cheaper MERV 11 option may be better suited. <a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text">↩</a></p>
</li>
<li id="fn:6">
<p>To cut down on waste, washable/reusable options are available, such as
<a href="https://www.knfilters.com/hvc-11625-hvac-filter-16-x-25-x-1">K&N Lifetime
series</a>. I had
not found any performance data in a CR-box, however will research further
if/when the filters need replacing. Sourcing them may be challenging. <a class="footnote-backref" href="#fnref:6" title="Jump back to footnote 6 in the text">↩</a></p>
</li>
</ol>
</div>Using Swift Package Manager with Carthage2016-12-01T00:00:00+00:002016-12-01T00:00:00+00:00Kyle Fullertag:fuller.li,2016-12-01:/posts/using-swift-package-manager-with-carthage/<p>Some users of my libraries have asked how they can integrate these libraries
into their applications using <a href="https://github.com/Carthage/Carthage">Carthage dependency
manager</a>.</p>
<p>Carthage requires libraries to provide an Xcode project or a binary framework.
However, since many of my Swift libraries are cross-platform, I am not
developing them in Xcode. In addition, distributing binary frameworks from
Swift source is not recommended by Apple because the <a href="http://www.bensnider.com/abi-compatibility-whoopdty-do-what-does-it-all-mean.html">Swift ABI is not yet
stable</a>.
Therefore the frameworks are tied to specific releases of Xcode.</p>
<blockquote>
<p>"While distribution and use of 3rd-party binary frameworks is not recommended …</p></blockquote><p>Some users of my libraries have asked how they can integrate these libraries
into their applications using <a href="https://github.com/Carthage/Carthage">Carthage dependency
manager</a>.</p>
<p>Carthage requires libraries to provide an Xcode project or a binary framework.
However, since many of my Swift libraries are cross-platform, I am not
developing them in Xcode. In addition, distributing binary frameworks from
Swift source is not recommended by Apple because the <a href="http://www.bensnider.com/abi-compatibility-whoopdty-do-what-does-it-all-mean.html">Swift ABI is not yet
stable</a>.
Therefore the frameworks are tied to specific releases of Xcode.</p>
<blockquote>
<p>"While distribution and use of 3rd-party binary frameworks is not recommended
(as mentioned in a <a href="https://developer.apple.com/swift/blog/?id=2">previous blog
post</a>), Swift supports
construction and distribution of frameworks in source form."
[<a href="https://developer.apple.com/swift/blog/?id=5">source</a>]</p>
</blockquote>
<p>Since Swift Package Manager offers the ability to generate an Xcode project, it
would allow Carthage to be able to build a Swift Package Manager dependency
with Xcode.</p>
<p>This post provides instructions on how to generate Xcode projects with Swift
Package Manager manually. In the future, I think it would be <a href="https://github.com/Carthage/Carthage/issues/1226">possible for
Carthage to do this
automatically</a> with changes
to Carthage if desired.</p>
<div class="note">
<p><strong>Note:</strong> These instructions should work for simple dependency graphs.
If you have the same dependency resolved by Swift Package Manger and Carthage
you may hit some problems.</p>
</div>
<h2>Installing your dependencies</h2>
<p>As the first step, you will need to add your Swift Package Manager dependency
into your <code>Cartfile</code>.</p>
<p>For example, if we want to integrate <a href="https://stencil.fuller.li">Stencil</a> and
<a href="https://github.com/kylef/Commander">Commander</a> we could use the following
<code>Cartfile</code>:</p>
<div class="highlight"><pre><span></span><code>github "kylef/Stencil" ~> 0.6.0
github "kylef/Commander" ~> 0.7.0
</code></pre></div>
<p>Then you can use Carthage to checkout and fetch the source of your dependencies.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>carthage<span class="w"> </span>update
</code></pre></div>
<p>Once you have the source of your Swift Package Manager dependencies you can use
Swift command line tool to generate an Xcode project for each dependency. The
command line tool provides an <code>generate-xcodeproj</code> subcommand.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>swift<span class="w"> </span>package<span class="w"> </span>generate-xcodeproj<span class="w"> </span>--help
OVERVIEW:<span class="w"> </span>Generates<span class="w"> </span>an<span class="w"> </span>Xcode<span class="w"> </span>project
OPTIONS:
<span class="w"> </span>--enable-code-coverage<span class="w"> </span>Enable<span class="w"> </span>code<span class="w"> </span>coverage<span class="w"> </span><span class="k">in</span><span class="w"> </span>the<span class="w"> </span>generated<span class="w"> </span>project
<span class="w"> </span>--output<span class="w"> </span>Path<span class="w"> </span>where<span class="w"> </span>the<span class="w"> </span>Xcode<span class="w"> </span>project<span class="w"> </span>should<span class="w"> </span>be<span class="w"> </span>generated
<span class="w"> </span>--xcconfig-overrides<span class="w"> </span>Path<span class="w"> </span>to<span class="w"> </span>xcconfig<span class="w"> </span>file
</code></pre></div>
<p>To generate Xcode projects for our example above, we can change
into the source directories and invoke <code>swift package generate-xcodeproj</code>:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="o">(</span><span class="nb">cd</span><span class="w"> </span>Carthage/Checkouts/Stencil<span class="w"> </span><span class="o">&&</span><span class="w"> </span>swift<span class="w"> </span>package<span class="w"> </span>generate-xcodeproj<span class="o">)</span>
$<span class="w"> </span><span class="o">(</span><span class="nb">cd</span><span class="w"> </span>Carthage/Checkouts/Commander<span class="w"> </span><span class="o">&&</span><span class="w"> </span>swift<span class="w"> </span>package<span class="w"> </span>generate-xcodeproj<span class="o">)</span>
</code></pre></div>
<p>Then you can use Carthage to build the dependencies.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>carthage<span class="w"> </span>build
***<span class="w"> </span>Building<span class="w"> </span>scheme<span class="w"> </span><span class="s2">"Commander"</span><span class="w"> </span><span class="k">in</span><span class="w"> </span>Commander.xcodeproj
***<span class="w"> </span>Building<span class="w"> </span>scheme<span class="w"> </span><span class="s2">"Stencil"</span><span class="w"> </span><span class="k">in</span><span class="w"> </span>Stencil.xcodeproj
</code></pre></div>
<p>Finally, you can integrate your dependencies into your application using
the instructions from <a href="https://github.com/Carthage/Carthage#adding-frameworks-to-an-application">Carthage on adding frameworks to an
application</a>.</p>
<p>For a macOS application, you would need to copy the frameworks that Carthage
built (which can be located in <code>Carthage/Builds/Mac</code>) into the "Embedded
Binaries" section of your Xcode project.</p>
<p><strong>NOTE</strong>: <em>You do not need to embed any of the dependencies testing frameworks
into your application.</em></p>Automated CocoaPod releases with CI2015-09-25T00:00:00+01:002015-09-25T00:00:00+01:00Kyle Fullertag:fuller.li,2015-09-25:/posts/automated-cocoapods-releases-with-ci/<p>Using continuous integration (CI), we can automate releasing our
<a href="https://cocoapods.org/">CocoaPods</a> to trunk. When someone tags a new release
and runs <code>git push</code>, CI will publish the pod to CocoaPods trunk.</p>
<p>This guide walks you though settings this up on both
<a href="https://circleci.com">Circle CI</a> and <a href="https://travis-ci.org">Travis CI</a>. If
you are not using these CI providers, you should be able to follow a similar
technique.</p>
<h3>CocoaPods trunk Access Token</h3>
<p>To release new or updates to existing pods you will need an access token
for trunk. This is normally created using the <code>pod trunk …</code></p><p>Using continuous integration (CI), we can automate releasing our
<a href="https://cocoapods.org/">CocoaPods</a> to trunk. When someone tags a new release
and runs <code>git push</code>, CI will publish the pod to CocoaPods trunk.</p>
<p>This guide walks you though settings this up on both
<a href="https://circleci.com">Circle CI</a> and <a href="https://travis-ci.org">Travis CI</a>. If
you are not using these CI providers, you should be able to follow a similar
technique.</p>
<h3>CocoaPods trunk Access Token</h3>
<p>To release new or updates to existing pods you will need an access token
for trunk. This is normally created using the <code>pod trunk register</code> command.</p>
<p>We're going to use this command to generate a unique token for our CI server to
perform release. Using the following:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pod<span class="w"> </span>trunk<span class="w"> </span>register<span class="w"> </span>kyle@fuller.li<span class="w"> </span>--description<span class="o">=</span><span class="s1">'CI Automation'</span>
<span class="o">[</span>!<span class="o">]</span><span class="w"> </span>Please<span class="w"> </span>verify<span class="w"> </span>the<span class="w"> </span>session<span class="w"> </span>by<span class="w"> </span>clicking<span class="w"> </span>the<span class="w"> </span>link<span class="w"> </span><span class="k">in</span><span class="w"> </span>the<span class="w"> </span>verification<span class="w"> </span>email<span class="w"> </span>that<span class="w"> </span>has<span class="w"> </span>been<span class="w"> </span>sent<span class="w"> </span>to<span class="w"> </span>kyle@fuller.li
</code></pre></div>
<p><strong>NOTE</strong>: <em>Remember to change my email address for the email address you use
with CocoaPods.</em></p>
<p>The next step will be to go to your email and "verify" the session by following
the link in your email.</p>
<blockquote>
<p>Hi Kyle,</p>
<p>Please confirm your CocoaPods session by clicking the following link:
<code>https://trunk.cocoapods.org/sessions/verify/uniquehash</code></p>
</blockquote>
<p>Once your session is verified, we can then grab the access token from CocoaPods to use in CI . CocoaPods stores the access token in the standard <code>~/.netrc</code> file.</p>
<p>We can pull this out using the following:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>grep<span class="w"> </span>-A2<span class="w"> </span><span class="s1">'trunk.cocoapods.org'</span><span class="w"> </span>~/.netrc
machine<span class="w"> </span>trunk.cocoapods.org
<span class="w"> </span>login<span class="w"> </span>kyle@fuller.li
<span class="w"> </span>password<span class="w"> </span>ef9bb4c41a4459ba92645a85b3c9cd88
</code></pre></div>
<p>You can see our token is <code>ef9bb4c41a4459ba92645a85b3c9cd88</code>, we now will need
to configure our CI server and set the environmental variable
<code>COCOAPODS_TRUNK_TOKEN</code> with the value of our token.</p>
<p><strong>IMPORTANT</strong>: <em>Once you have an access token for CI, you should
run <code>pod trunk register</code> again so you are locally using a separate token from the CI service.</em></p>
<h4>Circle CI</h4>
<p>On Circle CI, the access token can be configured in the settings for your repository on
their website.</p>
<p><img alt="Circle CI environmental variables" src="https://fuller.li/images/cocoapods-ci/circleci-env.png"></p>
<h4>Travis CI</h4>
<p>Like Circle CI, you can also add the access token for CocoaPods trunk in the settings for your repository like follows:</p>
<p><img alt="Travis CI environmental variables" src="https://fuller.li/images/cocoapods-ci/travisci-env.png"></p>
<p><strong>IMPORTANT</strong>: <em>Ensure that <code>Display value in build log</code> is not enabled.</em></p>
<p>You can also use the <code>travis</code> CLI tool to <a href="http://docs.travis-ci.com/user/encryption-keys/">encrypt environmental variables in your <code>.travis.yml</code> file</a>.</p>
<h3>Running <code>pod trunk push</code> when new releases are tagged</h3>
<p>The next step is to configure our CI process to push our pod when a new release
is tagged.</p>
<h4>Circle CI</h4>
<p>For Circle CI, we can add a <code>deployment</code> section to run the <code>pod trunk push</code> command on new tags.</p>
<div class="highlight"><pre><span></span><code><span class="nt">machine</span><span class="p">:</span>
<span class="w"> </span><span class="nt">xcode</span><span class="p">:</span>
<span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s">"7.0"</span>
<span class="nt">deployment</span><span class="p">:</span>
<span class="w"> </span><span class="nt">release</span><span class="p">:</span>
<span class="w"> </span><span class="nt">tag</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">/.*/</span>
<span class="w"> </span><span class="nt">commands</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pod trunk push</span>
</code></pre></div>
<h4>Travis CI</h4>
<p>Similarly on Travis CI, we can add a <code>deploy</code> section to run a custom script
<code>scripts/push.sh</code> on new tags.</p>
<div class="highlight"><pre><span></span><code><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">objective-c</span>
<span class="nt">osx_image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">xcode7</span>
<span class="nt">deploy</span><span class="p">:</span>
<span class="w"> </span><span class="nt">provider</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">script</span>
<span class="w"> </span><span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">./scripts/push.sh</span>
<span class="w"> </span><span class="nt">on</span><span class="p">:</span>
<span class="w"> </span><span class="nt">tags</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
</code></pre></div>
<p>You will need to create a <code>scripts/push.sh</code> file in your repository and commit
it so that Travis CI will configure rvm (ruby version manager), and then
run <code>pod trunk push</code> on deployment.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env bash</span>
<span class="nb">source</span><span class="w"> </span>~/.rvm/scripts/rvm
rvm<span class="w"> </span>use<span class="w"> </span>default
pod<span class="w"> </span>trunk<span class="w"> </span>push
</code></pre></div>
<h3>Pushing your Pod</h3>
<p>That's it, now when you push new versions to your CI server they will automatically be released to CocoaPods trunk!</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>edit<span class="w"> </span>Stencil.podspec
<span class="c1"># Update the version in the podspec</span>
$<span class="w"> </span>git<span class="w"> </span>add<span class="w"> </span>Stencil.podspec
$<span class="w"> </span>git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s1">'Release 1.0.0'</span>
$<span class="w"> </span>git<span class="w"> </span>tag<span class="w"> </span><span class="m">1</span>.0.0
$<span class="w"> </span>git<span class="w"> </span>push<span class="w"> </span>origin<span class="w"> </span>master<span class="w"> </span>--tags
</code></pre></div>
<p>Examples:</p>
<ul>
<li><a href="https://github.com/kylef/Stencil/blob/0.3.0/circle.yml#L10">Stencil</a> uses Circle CI for deployment</li>
<li><a href="https://github.com/kylef/Commander/blob/0.2.1/.travis.yml#L9">Commander</a> uses Travis CI for deployment</li>
</ul>
<h4>Note on Security</h4>
<p>It's important to note that we've uploaded an access token to our CI server.
Using this token it's possible to publish new, or update your pods.
Therefore it's extremely important to keep this token private and do not
share it with anyone.</p>
<p>In the event that your trunk session has been compromised, you can
invalidate any other sessions using the following:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pod<span class="w"> </span>trunk<span class="w"> </span>me<span class="w"> </span>clean-sessions<span class="w"> </span>--all
</code></pre></div>Migrating from OCUnit to XCTest2014-04-09T00:00:00+01:002014-04-09T00:00:00+01:00Kyle Fullertag:fuller.li,2014-04-09:/posts/migrating-from-ocunit-to-xctest/<p>You might think you were in luck when Xcode offered you an option to migrate to
XCTest, but shortly after trying you will discover that this isn't a true
migration. Xcode will only migrate your code, it will not update your project
to be testable with XCTest. There are still some steps left and unfortunately
Apple have not shared any documentation on how to do this.</p>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">I have later discovered that it does work as expected, but only if
you run the conversion from a project directly. If you try running the
conversion from a workspace you will get the problems mentioned above. I
have filed this as <a class="reference external" href="rdar://16581037">rdar://16581037</a> so Apple can fix this in the future. For
now, you'll need to follow the below steps.</p>
</div>
<p><br/></p>
<img alt="OCUnit is deprecated, Convert to XCTest" class="align-center" src="https://fuller.li/images/migrate-xctest/convert.png" style="width: 420px; height: 129px;"/>
<p><br/></p>
<p>You can follow these steps to migrate your project to XCTest once you've
migrated the code with the automatic converter:</p>
<p></p><p>You might think you were in luck when Xcode offered you an option to migrate to
XCTest, but shortly after trying you will discover that this isn't a true
migration. Xcode will only migrate your code, it will not update your project
to be testable with XCTest. There are still some steps left and unfortunately
Apple have not shared any documentation on how to do this.</p>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">I have later discovered that it does work as expected, but only if
you run the conversion from a project directly. If you try running the
conversion from a workspace you will get the problems mentioned above. I
have filed this as <a class="reference external" href="rdar://16581037">rdar://16581037</a> so Apple can fix this in the future. For
now, you'll need to follow the below steps.</p>
</div>
<p><br /></p>
<img alt="OCUnit is deprecated, Convert to XCTest" class="align-center" src="https://fuller.li/images/migrate-xctest/convert.png" style="width: 420px; height: 129px;" />
<p><br /></p>
<p>You can follow these steps to migrate your project to XCTest once you've
migrated the code with the automatic converter:</p>
<p></p>
<ol class="arabic">
<li><p class="first">Change the <cite>Wrapper Extension</cite> your project's test target in build
settings from <cite>ocunit</cite> to <cite>xctest</cite>.</p>
<blockquote>
<img alt="Test Target's Wrapper Extension" class="align-center" src="https://fuller.li/images/migrate-xctest/wrapper-extension.png" style="width: 636px; height: 319px;" />
</blockquote>
</li>
<li><p class="first">This is the scary part, you are going to need to manually edit your Xcode
project by hand and replace the product type for your test target. Firstly,
close Xcode and then open the project's <cite>project.pbxproj</cite> in your favourite
text editor and search for the product type. You will need to change it from
<cite>com.apple.product-type.bundle</cite> to <cite>com.apple.product-type.bundle.unit-test</cite>.</p>
<blockquote>
<div class="highlight"><pre><span></span><span class="w"> </span>productReference = 77DC06B215702EDB0001EF8C /* PalaverTests.xctest */
<span class="gd">-productType = "com.apple.product-type.bundle";</span>
<span class="gi">+productType = "com.apple.product-type.bundle.unit-test";</span>
</pre></div>
</blockquote>
</li>
<li><p class="first">Remove the OCUnit <cite>Run Script</cite> build phase from your project's test target.</p>
<blockquote>
<img alt="OCUnit Run Script Build Phase" class="align-center" src="https://fuller.li/images/migrate-xctest/run-script-build-phase.png" style="width: 636px; height: 319px;" />
</blockquote>
</li>
<li><p class="first">Replace <cite>SenTestingKit</cite> framework with <cite>XCTest</cite> inside the test target's
link with libraries build phase. Even better, you can <a class="reference external" href="http://tonyarnold.com/2014/04/10/clean-up-your-projects-with-xcode-5.html">clean up your project</a>
by enabling modules instead of adding XCTest.</p>
<blockquote>
<img alt="Replacing SenTestkingKit for XCTest framework" class="align-center" src="https://fuller.li/images/migrate-xctest/frameworks.png" style="width: 636px; height: 319px;" />
</blockquote>
</li>
<li><p class="first">For iOS, you may need to add the SDK's developer frameworks so the linker
can find the XCTest framework for iOS when building the project.</p>
<p>You will need to add <cite>$(SDKROOT)/Developer/Library/Frameworks</cite> to the
framework search paths for the test target.</p>
<blockquote>
<img alt="Adding Developer frameworks to search paths" class="align-center" src="https://fuller.li/images/migrate-xctest/framework-search.png" style="width: 676px; height: 316px;" />
</blockquote>
</li>
</ol>
<div class="section" id="kiwi">
<h2>Kiwi</h2>
<p>If you are using Kiwi, be sure switch to the <cite>XCTest</cite> pod.</p>
<div class="highlight"><pre><span></span><span class="n">pod</span><span class="w"> </span><span class="s1">'Kiwi/XCTest'</span>
</pre></div>
</div>
Memory Management with ARC2013-10-24T00:00:00+01:002013-10-24T00:00:00+01:00kyleftag:fuller.li,2013-10-24:/posts/memory-management-arc/<p>This blog post accompanies a talk I recently gave at NSLondon, you can find the
slides for this talk <a class="reference external" href="https://speakerdeck.com/kylef/memory-management">here</a>.
There will also be a video version of this out shortly, I will post a link once
it's out on my <a class="reference external" href="https://twitter.com/kylefuller">Twitter</a>.</p>
<p>ARC is a very powerful feature introduced in Xcode in 4.0, along with various
run-time enhancements. With the introduction of ARC, a lot has changed under the
hood. The Objective-C run-time has a completely new interface for using many of the
old concepts such as autorelease pools …</p><p>This blog post accompanies a talk I recently gave at NSLondon, you can find the
slides for this talk <a class="reference external" href="https://speakerdeck.com/kylef/memory-management">here</a>.
There will also be a video version of this out shortly, I will post a link once
it's out on my <a class="reference external" href="https://twitter.com/kylefuller">Twitter</a>.</p>
<p>ARC is a very powerful feature introduced in Xcode in 4.0, along with various
run-time enhancements. With the introduction of ARC, a lot has changed under the
hood. The Objective-C run-time has a completely new interface for using many of the
old concepts such as autorelease pools. There is also a new interface for
retaining and releasing objects.</p>
<p>This post will briefly explain these concepts, along with how they work in the
runtime. It will include any common mistakes such as retain cycles, the dealloc
problem and <cite>copy</cite> vs <cite>strong</cite> with blocks.</p>
<div class="section" id="retainable-object-pointers">
<h2>Retainable Object Pointers</h2>
<p>Automatic Reference Counting (ARC) is a tool for managing retainable object
pointers. This is any kind of pointer to an object which can handle the
Objective-C machinery for sending the <cite>retain</cite>, <cite>release</cite> and <cite>autorelease</cite>
messages.</p>
<p>These messages are not sent in the way they would have been done pre-ARC, but
instead using the <cite>objc_retain</cite>, <cite>objc_release</cite> and <cite>objc_retainAutorelease</cite>
functions.</p>
<p>Retainable types include <cite>id</cite>, <cite>Class</cite> and <cite>NSObject</cite>. Along with
<cite>__attribute__((NSObject))</cite>.</p>
<p><cite>__attribute__((NSObject))</cite> is an attribute which allows you to declare that a
typedef can support memory management by ARC. That the <cite>objc_retain</cite>,
<cite>objc_release</cite> and <cite>objc_retainAutorelease</cite> function is supported with this
type and that it can retain and release memory as expected.</p>
<p><cite>dispatch_queue_t</cite> is a perfect example of this. Starting in iOS 6 and OS X
10.8, this type (and many other types) support memory management by ARC.
Therefore you can use use these types with the strong property referencing.</p>
<div class="highlight"><pre><span></span><span class="k">@property</span><span class="w"> </span><span class="p">(</span><span class="k">nonatomic</span><span class="p">,</span><span class="w"> </span><span class="k">strong</span><span class="p">)</span><span class="w"> </span><span class="n">dispatch_queue_t</span><span class="w"> </span><span class="n">completionBlock</span><span class="p">;</span>
</pre></div>
</div>
<div class="section" id="ownership-qualification">
<h2>Ownership Qualification</h2>
<p>What gives ARC it's immense power, is the fact that you can mark different
pieces of memory with qualifiers to indicate how it should be handled.
Weather it be strongly holding them with the <cite>__strong</cite> qualifier or not
doing any management at all with the <cite>__unsafe_unretained</cite> qualifier.</p>
<div class="section" id="unsafe-unretained">
<h3>__unsafe_unretained</h3>
<p>__unsafe_unretained is by far the most simple. It basically means that no
memory management will be done. It can be used for both objects and scalar
types such as <cite>int</cite> and <cite>BOOL</cite>.</p>
</div>
<div class="section" id="strong">
<h3>__strong</h3>
<p>The strong qualifier will <strong>strongly</strong> hold an object. It does so by
retaining an object when one is set to a strong pointer. When this pointer is
set to nil, the object is released.</p>
<p>For example, the following two piece of code will do nearly identical things.</p>
<div class="highlight"><pre><span></span><span class="c1">// Without ARC</span>
<span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">setName:</span><span class="p">(</span><span class="bp">NSString</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="nv">name</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">[</span><span class="n">_name</span><span class="w"> </span><span class="k">release</span><span class="p">];</span>
<span class="w"> </span><span class="n">_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span>
<span class="w"> </span><span class="p">[</span><span class="n">_name</span><span class="w"> </span><span class="k">retain</span><span class="p">];</span>
<span class="p">}</span>
<span class="c1">// With ARC</span>
<span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">setName:</span><span class="p">(</span><span class="bp">NSString</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="nv">name</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<!-- ** -->
</div>
<div class="section" id="weak">
<h3>__weak</h3>
<p>Weak is an interesting qualification. It allows you to reference an
object, when the object is deallocated the pointer will become nil.
Instead of retaining an object, the object's retain count will not change
with weak referencing.</p>
<p>Because weak objects can be released at any point of time, it's often
important to create a strong reference to it. This allows you to keep it
alive while it's in use.</p>
<p>Most of the work for weak referencing is done in the new run-time support
in Objective-C, introduced in LLVM along with ARC.</p>
<p>When setting a weak pointer, ARC will call <cite>id objc_storeWeak(id *object, id
value)</cite> which simply adds the pointer (object) to the <cite>value</cite> object's table of
weak references.</p>
<p>Now, when the object <cite>value</cite> is deallocated, it will iterate over every object
in the weak referencing table and set those weak references to nil.</p>
<p>Alternatively, the <cite>objc_destroyWeak(id *object)</cite> function is used to delete
this pointer from the object's weak reference table. This is normally called
from an object's dealloc.</p>
<p>Because weak has to maintain this table of references, it adds the necessary
overhead to do this. I have done some testing and it shows that weak referencing
can take over three times as long as the normal strong referencing.</p>
<div class="section" id="objc-arc-weak-unavailable">
<h4>objc_arc_weak_unavailable</h4>
<p>Using the <cite>objc_arc_weak_unavailable</cite> attribute, you can mark an object so that
it cannot be used with the weak qualification. This may be handy for types
which use <cite>__attribute__((NSObject))</cite>.</p>
</div>
</div>
<div class="section" id="autoreleasing">
<h3>__autoreleasing</h3>
<p>Autoreleasing has been around for a while, however it has changed a lot with
the introduction of ARC. You can no longer use the <cite>NSAutoreleasePool</cite> class.</p>
<p>Instead, you can use the <cite>objc_autoreleaseReturnValue(id value)</cite> function to
autorelease an object. This will retain the object and then return it. While
it will also add it to the current release pool.</p>
<p>To drain the release pool the <cite>objc_autoreleasePoolPop(void *pool)</cite> function is called.</p>
</div>
</div>
<div class="section" id="blocks">
<h2>Blocks</h2>
<p>I often see this question of <cite>strong</cite> vs <cite>copy</cite> for blocks and there is a lot
of confusion about what you should be using.</p>
<p>In Apple's transitioning to ARC guide, they mention this:</p>
<blockquote>
Blocks "just work" when you pass blocks up the stack in ARC mode, such as
in a return. You don’t have to call Block Copy any more. You still need
to use <cite>[^{} copy]</cite> when passing "down" the stack into <cite>arrayWithObjects:</cite>
and other methods that do a retain.</blockquote>
<p>Blocks will be stored in the current stack, this means they are available in
the local scope. If you use them outside, you must make a copy of the block.
Otherwise ARC will retain these objects, and then when they go out of memory
you'll have a pointer to something that has been released.</p>
<p>This is not normally a problem, often you will want to run a block from the
current stack. However, sometimes you want to use them with properties and that
might mean you will want to use it outside of the current stack. Therefore it
is important to take a copy.</p>
<div class="highlight"><pre><span></span><span class="k">@property</span><span class="w"> </span><span class="p">(</span><span class="k">nonatomic</span><span class="p">,</span><span class="w"> </span><span class="k">copy</span><span class="p">)</span><span class="w"> </span><span class="n">dispatch_block_t</span><span class="w"> </span><span class="n">block</span><span class="p">;</span>
</pre></div>
<p>You might also want to convert a block type to <cite>id</cite> for use in an array or
something similar. You will also need to make a copy, for example:</p>
<div class="highlight"><pre><span></span><span class="n">dispatch_block_t</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">^</span><span class="p">{</span>
<span class="w"> </span><span class="n">NSLog</span><span class="p">(</span><span class="s">@"Hello World!"</span><span class="p">);</span>
<span class="p">};</span>
<span class="bp">NSArray</span><span class="w"> </span><span class="o">*</span><span class="n">blocks</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[[</span><span class="bp">NSArray</span><span class="w"> </span><span class="n">alloc</span><span class="p">]</span><span class="w"> </span><span class="n">initWithObject</span><span class="o">:</span><span class="p">[</span><span class="n">block</span><span class="w"> </span><span class="k">copy</span><span class="p">]];</span>
</pre></div>
<!-- ** -->
</div>
<div class="section" id="retain-cycle">
<h2>Retain Cycle</h2>
<p>A retain cycle is an issue where you have a retain-able object which
indirectly has a strong reference to itself. Usually using another block or
object.</p>
<p>For example:</p>
<div class="highlight"><pre><span></span><span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">startOperation</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="bp">NSOperation</span><span class="w"> </span><span class="o">*</span><span class="n">operation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[[</span><span class="bp">NSOperation</span><span class="w"> </span><span class="n">alloc</span><span class="p">]</span><span class="w"> </span><span class="n">init</span><span class="p">];</span>
<span class="w"> </span><span class="p">[</span><span class="n">operation</span><span class="w"> </span><span class="n">setCompletionBlock</span><span class="o">:^</span><span class="p">{</span>
<span class="w"> </span><span class="n">NSLog</span><span class="p">(</span><span class="s">@"Completion for %@"</span><span class="p">,</span><span class="w"> </span><span class="n">operation</span><span class="p">);</span>
<span class="w"> </span><span class="p">}];</span>
<span class="p">}</span>
</pre></div>
<!-- ** -->
<p>The above example shows a completion block which has a strong reference to the
operation. For the lifetime of this block, the operation will stay alive.</p>
<p>You will notice, that the operation strongly holds onto the completion block
too. Which means that the completion block will be alive for the lifespan of
the operation.</p>
<p>It's clear we have a problem, ARC won't ever be able to release this object.</p>
<p>The solution would be to use a weak reference to the operation:</p>
<div class="highlight"><pre><span></span><span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">startOperation</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="bp">NSOperation</span><span class="w"> </span><span class="o">*</span><span class="n">operation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[[</span><span class="bp">NSOperation</span><span class="w"> </span><span class="n">alloc</span><span class="p">]</span><span class="w"> </span><span class="n">init</span><span class="p">];</span>
<span class="w"> </span><span class="k">__weak</span><span class="w"> </span><span class="bp">NSOperation</span><span class="w"> </span><span class="o">*</span><span class="n">weakOperation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">operation</span><span class="p">;</span>
<span class="w"> </span><span class="p">[</span><span class="n">operation</span><span class="w"> </span><span class="n">setCompletionBlock</span><span class="o">:^</span><span class="p">{</span>
<span class="w"> </span><span class="n">NSLog</span><span class="p">(</span><span class="s">@"Completion for %@"</span><span class="p">,</span><span class="w"> </span><span class="n">weakOperation</span><span class="p">);</span>
<span class="w"> </span><span class="p">}];</span>
<span class="p">}</span>
</pre></div>
<!-- ** -->
</div>
<div class="section" id="the-deallocation-problem">
<h2>The Deallocation Problem</h2>
<p>One of the hardest problems with ARC comes to deallocating your objects safely.
It's common to retain objects in the background. When a secondary thread has
the last reference to an object. It will be responsible for deallocating the
object.</p>
<p>When this object is one of the many UIKit objects, such as a view controller.
This can cause a real problem if it's deallocated in the background. It's often
very difficult to both debug, and to reproduce this issue since it's a race
condition.</p>
<p>To help prevent this problem, you should always use __weak when referencing
UIKit objects in the background.</p>
<p>Apple have described this problem on
<a class="reference external" href="https://developer.apple.com/library/ios/technotes/tn2109/_index.html">TN2109</a>.</p>
</div>
<div class="section" id="corefoundation">
<h2>CoreFoundation</h2>
<p>CoreFoundation objects are not subject to ARC. You still have to maintain these
objects like you have done in the past.</p>
<div class="highlight"><pre><span></span><span class="n">CGRelease</span><span class="p">(</span><span class="n">stringRef</span><span class="p">);</span>
</pre></div>
<p>Remember to be careful when using CG objects, especially if they are not owned
by you. In the following example, we are retrieving a CGColor reference
from UIColor. UIColor owns this reference, and is responsible for memory
management.</p>
<div class="highlight"><pre><span></span><span class="bp">UIColor</span><span class="w"> </span><span class="o">*</span><span class="n">whiteColor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="bp">UIColor</span><span class="w"> </span><span class="n">whiteColor</span><span class="p">];</span>
<span class="n">CGColorRef</span><span class="w"> </span><span class="n">whiteRef</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">whiteColor</span><span class="w"> </span><span class="n">CGColor</span><span class="p">];</span>
<span class="c1">// Crash when using whiteRef</span>
</pre></div>
<!-- ** -->
<p>This example will result in ARC releasing UIColor after line 2 because it is no
longer used. The whiteRef will now be a pointer to a piece of memory which may
have been released at this stage.</p>
<p>Instead you should use the following code, which will retain this reference for
ourself.</p>
<div class="highlight"><pre><span></span><span class="bp">UIColor</span><span class="w"> </span><span class="o">*</span><span class="n">whiteColor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="bp">UIColor</span><span class="w"> </span><span class="n">whiteColor</span><span class="p">];</span>
<span class="n">CGColorRef</span><span class="w"> </span><span class="n">whiteRef</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CGRetain</span><span class="p">([</span><span class="n">whiteColor</span><span class="w"> </span><span class="n">CGColor</span><span class="p">]);</span>
<span class="c1">// Use whiteRef</span>
<span class="n">CGRelease</span><span class="p">(</span><span class="n">whiteRef</span><span class="p">);</span>
</pre></div>
<!-- ** -->
</div>
<div class="section" id="exceptions">
<h2>Exceptions</h2>
<p>By default, exceptions are not ARC safe. Conventionally, an exception in
Objective-C represents an unrecoverable error. So ARC not being exception safe
is perfectly fine and acceptable behaviour. However, there is still a way to
enable it using the <cite>-fobjc-arc-exceptions</cite> compiler flag.</p>
<p>You can enable a compiler option to handle exceptions properly with ARC.
But you probably shouldn't do this!</p>
</div>
Versioning with Xcode and git2013-08-18T00:00:00+01:002013-08-18T00:00:00+01:00Kyle Fullertag:fuller.li,2013-08-18:/posts/versioning-with-xcode-and-git/<p>Wouldn't it be great if your iOS or OS X projects just take their version and
build number automatically from git? Well, it can!</p>
<p>Using a script in your build phase, you can run a shell to determine the
version number and inject this into the <cite>Info.plist</cite> of a build. It will
never modify your local project, just the created build.</p>
<div class="highlight"><pre><span></span><span class="nv">GIT_RELEASE_VERSION</span><span class="o">=</span><span class="k">$(</span>git<span class="w"> </span>describe<span class="w"> </span>--tags<span class="w"> </span>--always<span class="w"> </span>--dirty<span class="k">)</span>
<span class="nv">COMMITS</span><span class="o">=</span><span class="k">$(</span>git<span class="w"> </span>rev-list<span class="w"> </span>HEAD<span class="w"> </span><span class="p">|</span><span class="w"> </span>wc<span class="w"> </span>-l<span class="k">)</span>
<span class="nv">COMMITS</span><span class="o">=</span><span class="k">$((</span><span class="nv">$COMMITS</span><span class="k">))</span>
defaults<span class="w"> </span>write<span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">BUILT_PRODUCTS_DIR</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">INFOPLIST_PATH</span><span class="p">%.*</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="s2">"CFBundleShortVersionString"</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">GIT_RELEASE_VERSION</span><span class="p">#*v</span><span class="si">}</span><span class="s2">"</span>
defaults<span class="w"> </span>write<span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">BUILT_PRODUCTS_DIR</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">INFOPLIST_PATH</span><span class="p">%.*</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="s2">"CFBundleVersion"</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">COMMITS</span><span class="si">}</span><span class="s2">"</span>
</pre></div>
<p></p><p>Wouldn't it be great if your iOS or OS X projects just take their version and
build number automatically from git? Well, it can!</p>
<p>Using a script in your build phase, you can run a shell to determine the
version number and inject this into the <cite>Info.plist</cite> of a build. It will
never modify your local project, just the created build.</p>
<div class="highlight"><pre><span></span><span class="nv">GIT_RELEASE_VERSION</span><span class="o">=</span><span class="k">$(</span>git<span class="w"> </span>describe<span class="w"> </span>--tags<span class="w"> </span>--always<span class="w"> </span>--dirty<span class="k">)</span>
<span class="nv">COMMITS</span><span class="o">=</span><span class="k">$(</span>git<span class="w"> </span>rev-list<span class="w"> </span>HEAD<span class="w"> </span><span class="p">|</span><span class="w"> </span>wc<span class="w"> </span>-l<span class="k">)</span>
<span class="nv">COMMITS</span><span class="o">=</span><span class="k">$((</span><span class="nv">$COMMITS</span><span class="k">))</span>
defaults<span class="w"> </span>write<span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">BUILT_PRODUCTS_DIR</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">INFOPLIST_PATH</span><span class="p">%.*</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="s2">"CFBundleShortVersionString"</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">GIT_RELEASE_VERSION</span><span class="p">#*v</span><span class="si">}</span><span class="s2">"</span>
defaults<span class="w"> </span>write<span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">BUILT_PRODUCTS_DIR</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">INFOPLIST_PATH</span><span class="p">%.*</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="s2">"CFBundleVersion"</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">COMMITS</span><span class="si">}</span><span class="s2">"</span>
</pre></div>
<p></p>
<p>Once you have the build phase in place, your version number will be created
using the last git tag. If the build was generated right after creating a git
tag. Then the version will simply be the tag. If you have made a few commits
after tagging, then you will will get a version number suffixed with the amount
of commits since the last tag, and the current hash. The hash is important
because it allows you to get back to that commit and find the exact code base
from a version number.</p>
<p>I always create a new tag before releasing to the app store, so any released
applications will have a nice clean version number such as <cite>1.0</cite> or <cite>1.2</cite>. Any
pre-release builds will often include the previous tag along with how many
commits since this tag and the hash. These builds should look something like
<cite>1.2-52-g8bdae1d</cite>.</p>
<div class="section" id="adding-the-build-phase-to-your-project">
<h2>Adding the build phase to your project</h2>
<p>To add this build phase to your Xcode project, go into your project file and
select the target you want to use. Then hit <strong>Editor > Add Build Phase > Add
Run Script Build Phase</strong>. Next, you will need to copy the above shell script
into the newly created build phase.</p>
<p>Afterwards, your project should contain a phase looking like this:</p>
<img alt="Adding the build phase to your project" class="align-center" src="https://fuller.li/images/versioning-with-xcode.png" style="width: 671px; height: 563px;" />
<p>There are two components to versioning. Firstly there is the version string
which was described above. There is also a version number, this has to be a
number which is always incremented on any build going to the app store.</p>
<p>If you have uncommitted files, the version string will be suffixed by <cite>-dirty</cite>
to indicate it's dirty. You can disable this by removing <cite>--dirty</cite> from the
first line in the build phase. However I find it useful to know if the build
has uncommitted changes.</p>
<p>To conclude, here are some possible examples:</p>
<ul class="simple">
<li><cite>1.0</cite> from a build directly from a tag.</li>
<li><cite>1.0-dirty</cite> from a build directly from a tag, with uncommitted code.</li>
<li><cite>1.0-2-g8bdae1d</cite> from a build on commit <cite>g8bdae</cite> which is 2 commits in
front of the <cite>1.0</cite> tag.</li>
<li><cite>g8bdae1d</cite> which would indicate that there are no tags.</li>
</ul>
</div>
How does BrowserID work?2012-05-07T23:01:04+01:002012-05-07T23:01:04+01:00Kyle Fullertag:fuller.li,2012-05-07:/posts/how-does-browserid-work/<p>This article will explain how the cryptography behind BrowserID works, and
lightly cover why BrowserID is a better alternative to OpenID.</p>
<p>A website will ask your browser for a BrowserID assertion via JavaScript.
This will either use a native BrowserID API in your browser, or it will use
the JavaScript implementation of BrowserID. This is known as the user agent.</p>
<div class="highlight"><pre><span></span><span class="nx">navigator</span><span class="p">.</span><span class="nx">id</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">gotAssertion</span><span class="p">);</span>
</pre></div>
<p>For supporting browsers, the user agent will be supplied and the
<cite>navigator.id.get</cite> method will exist. The user agent can be provided by either
the …</p><p>This article will explain how the cryptography behind BrowserID works, and
lightly cover why BrowserID is a better alternative to OpenID.</p>
<p>A website will ask your browser for a BrowserID assertion via JavaScript.
This will either use a native BrowserID API in your browser, or it will use
the JavaScript implementation of BrowserID. This is known as the user agent.</p>
<div class="highlight"><pre><span></span><span class="nx">navigator</span><span class="p">.</span><span class="nx">id</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">gotAssertion</span><span class="p">);</span>
</pre></div>
<p>For supporting browsers, the user agent will be supplied and the
<cite>navigator.id.get</cite> method will exist. The user agent can be provided by either
the browser, an extension to the browser, or via a JavaScript include on the
website to use a JavaScript implementation.</p>
<div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"https://browserid.org/include.js"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/javascript"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
</pre></div>
<p>This means that a browser or browser extension can provide their own
implementation, or a JavaScript implementation can be used in unsupported
browsers.</p>
<p>Out of the process, once the browser has an assertion. It will provide the
assertion to the <cite>gotAssertion</cite> callback. The website can then provide this
back to the web server to validate the assertion to confirm the user's identity.</p>
<div class="section" id="what-is-an-assertion">
<h2>What is an assertion?</h2>
<p>An assertion is a string which holds a JSON structure including an identity
assertion and an identity certificate. The assertion will be generated by the
browser for an identity, the browser will need to get an identity certificate
from a provider to ensure they own that identity. This identity can be used
again and again until it expires.</p>
<div class="section" id="identity-certificate">
<h3>Identity certificate</h3>
<p>An identity certificate will look like the following:</p>
<div class="highlight"><pre><span></span><span class="p">{</span>
<span class="w"> </span><span class="s2">"iss"</span><span class="o">:</span><span class="w"> </span><span class="s2">"kylefuller.co.uk"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"exp"</span><span class="o">:</span><span class="w"> </span><span class="s2">"1313971280961"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"public-key"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">...</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="s2">"principal"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s2">"email"</span><span class="o">:</span><span class="w"> </span><span class="s2">"inbox@kylefuller.co.uk"</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>The "iss" field will include the domain of the issuer. The code checking the
assertion should verify that is either the domain used in the email address, or
a trusted fallback provider, such as <cite>browserid.org</cite>. This means that either
the domain providing your email address or <cite>browserid.org</cite> can provide a valid
identity certificate for your email.</p>
<p>The fallback provider will allow you to use that provider instead of your email
provider in the situation that your email provider does not support BrowserID.</p>
<p>The above identity certificate signed by the issuer, and their certificate
should be available over HTTPS at <cite>https://iss/.well-known/browserid</cite>.</p>
<p>The public key in the identity certificate is the public key of the user. This
key can be generated by the browser so only the user will have control over it.</p>
</div>
<div class="section" id="identity-assertion">
<h3>Identity assertion</h3>
<p>The identity assertion is the final piece in a BrowserID assertion. It will be
signed by the browser's private key, so it will match the public key in the
identity certificate which our provider has confirmed we own.</p>
<div class="highlight"><pre><span></span><span class="p">{</span>
<span class="w"> </span><span class="s2">"exp"</span><span class="o">:</span><span class="w"> </span><span class="mf">1320280579437</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"aud"</span><span class="o">:</span><span class="w"> </span><span class="s2">"https://demand-browserid.herokuapp.com"</span>
<span class="p">}</span>
</pre></div>
<p>The identity assertion provides an expiry and an audience. The identity
assertion allows a website to confirm the assertion is meant for them, the
audience should be the website that we want to sign-in to.</p>
<p>The relying party which the user is identifying to should check both the
identity certificate and the identity assertion to check the certificates
match, and that neither has expired. If these are valid assertions for our
audience, then we can confirm the user owns the email address and in the
identity certificate.</p>
</div>
</div>
<div class="section" id="why-use-browserid-whats-wrong-with-openid">
<h2>Why use BrowserID, whats wrong with OpenID?</h2>
<p>There are many benefits of BrowserID over OpenID.</p>
<ul class="simple">
<li>BrowserID is far simpler to use than OpenID for an average user. It is also
quicker, since you do not need to type your OpenID into each site.</li>
<li>When you sign-in to a site using OpenID, your OpenID provider knows you are
visiting that site. With BrowserID, your BrowserID provider is not aware of
the site you are visiting since your assertion is created in the browser. You
have already retrieved your identity certificate from the provider. Your
privacy is protected with BrowserID.</li>
<li>BrowserID uses an email address, any existing site will already have your
email address. This would allow a site to start using BrowserID without
requiring you to provide any additional information.</li>
</ul>
</div>
Organising dotfiles in a git repository2012-03-28T13:48:07+01:002012-03-28T13:48:07+01:00Kyle Fullertag:fuller.li,2012-03-28:/posts/organising-dotfiles-in-a-git-repository/<p>Organising dotfiles can be done in numerous ways. Many dotfile repositories
often have their own clunky script to copy or symbolically link their dotfiles
in place. I feel this is a dirty approach and I prefer my files to be easily
manageable via the git command. I don't want to have to copy a file every time
I change it.</p>
<img alt="Screenshot of organising your dotfiles" class="align-center" src="https://fuller.li/images/dotfiles.png" style="width: 589px; height: 276px;" />
<p>Another approach I have seen done, is making your whole home directory a git
repository. Unfortunately after using this solution you will come across a
number of flaws, any repository …</p><p>Organising dotfiles can be done in numerous ways. Many dotfile repositories
often have their own clunky script to copy or symbolically link their dotfiles
in place. I feel this is a dirty approach and I prefer my files to be easily
manageable via the git command. I don't want to have to copy a file every time
I change it.</p>
<img alt="Screenshot of organising your dotfiles" class="align-center" src="https://fuller.li/images/dotfiles.png" style="width: 589px; height: 276px;" />
<p>Another approach I have seen done, is making your whole home directory a git
repository. Unfortunately after using this solution you will come across a
number of flaws, any repository in your home directory will now follow your
<cite>~/.gitignore</cite>. I also came into many problems when I was using Xcode which
will try to git add projects into your dotfiles repository. Like symbolic
linking all my dotfiles into place, this solution also felt clunky.</p>
<p>Git allows you to seperate the work tree and the git dir via environment
variables or arguments to the git command. This allows us to store the bare git
dir in <cite>~/.files.git</cite> while still keeping our entire home directory as the work
tree for git.</p>
<p>I settled for using a simple alias in my <cite>.zshrc</cite> which allows me to easily use
git to manage my dotfiles. But, it is imporant to remember that the home
directory will not be seen as a git repository unless you use this alias. You
won't be able to accidentally use git commands thinking you were in another
repo, (or accidentally git add a bunch of things in a mercurial repository).</p>
<p>Here is the alias I use:</p>
<div class="highlight"><pre><span></span>$<span class="w"> </span><span class="nb">alias</span><span class="w"> </span><span class="nv">home</span><span class="o">=</span>git<span class="w"> </span>--work-tree<span class="o">=</span><span class="nv">$HOME</span><span class="w"> </span>--git-dir<span class="o">=</span><span class="nv">$HOME</span>/.files.git
</pre></div>
<p>You can use this alias just as you would use the normal git command, this
allows you to clone and init a repo. To clone a dotfiles repo, you can do:</p>
<div class="highlight"><pre><span></span>$<span class="w"> </span>home<span class="w"> </span>clone<span class="w"> </span>git://github.com/kylef/dotfiles.git
</pre></div>
Monitoring InspIRCd with MRTG2011-08-13T00:00:00+01:002011-08-13T00:00:00+01:00Kyle Fullertag:fuller.li,2011-08-13:/posts/monitoring-inspircd-mrtg/<p>This guide will show you how to configure MRTG to show statistics from an
InspIRCd IRC server, this will include user counts and channel counts. This
guide assumes you already have InspIRCd and MRTG setup and working. <s>You can
see an example of this working at <a href="http://hydra.sector5d.org/">Sector 5D</a>.</s></p>
<h3>InspIRCd config</h3>
<p>First, you will need to configure InspIRCd to use the <code>m_http</code> and
<code>m_http_stats</code> module. I configure InspIRCd to listen on localhost and port
8081 with SSL, but you could pick any port you want, just remember to change
the <code>$address …</code></p><p>This guide will show you how to configure MRTG to show statistics from an
InspIRCd IRC server, this will include user counts and channel counts. This
guide assumes you already have InspIRCd and MRTG setup and working. <s>You can
see an example of this working at <a href="http://hydra.sector5d.org/">Sector 5D</a>.</s></p>
<h3>InspIRCd config</h3>
<p>First, you will need to configure InspIRCd to use the <code>m_http</code> and
<code>m_http_stats</code> module. I configure InspIRCd to listen on localhost and port
8081 with SSL, but you could pick any port you want, just remember to change
the <code>$address</code> variable inside <code>inspircd-stats.sh</code> later.</p>
<p>Note, the following config is for InspIRCd 2.0, if you use a older version you
may need to change the bind line to a http line, please see the InspIRCd wiki
for using a older version.</p>
<div class="highlight"><pre><span></span><code><span class="nt"><module</span><span class="w"> </span><span class="na">name=</span><span class="s">"m_httpd.so"</span><span class="nt">></span>
<span class="nt"><module</span><span class="w"> </span><span class="na">name=</span><span class="s">"m_httpd_stats.so"</span><span class="nt">></span>
<span class="nt"><bind</span><span class="w"> </span><span class="na">address=</span><span class="s">"localhost"</span><span class="w"> </span><span class="na">port=</span><span class="s">"8081"</span><span class="w"> </span><span class="na">type=</span><span class="s">"httpd"</span><span class="w"> </span><span class="na">ssl=</span><span class="s">"gnutls"</span><span class="nt">></span>
</code></pre></div>
<p>Once you have placed this in your InspIRCd config, you can rehash or reload
InspIRCd and then you will be able to connect to it over http(s) to retrive
stats. Such as at https://localhost:8081/stats</p>
<h4>Creating a script to gather the user or channel count on InspIRCd</h4>
<p>The following script queries the InspIRCd <code>m_httpd_stats</code> module for the user
or channel count. This depends on <a href="http://xmlstar.sourceforge.net/">XMLStarlet</a>
(<a href="http://packages.debian.org/search?keywords=xmlstarlet">Debian package</a>, <a href="https://aur.archlinux.org/packages.php?ID=20101">Arch
AUR Package</a>), so please
install this first.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/sh</span>
<span class="nv">address</span><span class="o">=</span><span class="s2">"https://localhost:8081/stats"</span>
<span class="nv">count_type</span><span class="o">=</span><span class="nv">$1</span>
<span class="k">if</span><span class="w"> </span><span class="o">[[</span><span class="w"> </span><span class="s2">"</span><span class="nv">$count_type</span><span class="s2">"</span><span class="w"> </span>!<span class="o">=</span><span class="w"> </span><span class="s2">"user"</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="s2">"</span><span class="nv">$count_type</span><span class="s2">"</span><span class="w"> </span>!<span class="o">=</span><span class="w"> </span><span class="s2">"channel"</span><span class="w"> </span><span class="o">]]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Usage: </span><span class="nv">$0</span><span class="s2"> <user|channel>"</span>
<span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">1</span>
<span class="k">elif</span>
<span class="nv">count</span><span class="o">=</span><span class="k">$(</span>wget<span class="w"> </span>-q<span class="w"> </span>-Y<span class="w"> </span>off<span class="w"> </span>-O<span class="w"> </span>-<span class="w"> </span>--no-check-certificate<span class="w"> </span><span class="nv">$address</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>xml<span class="w"> </span>sel<span class="w"> </span>-t<span class="w"> </span>-v<span class="w"> </span><span class="s2">"inspircdstats/general/</span><span class="si">${</span><span class="nv">count_type</span><span class="si">}</span><span class="s2">count"</span><span class="k">)</span>
<span class="nb">echo</span><span class="w"> </span><span class="nv">$count</span>
<span class="nb">echo</span><span class="w"> </span><span class="nv">$count</span>
<span class="nb">echo</span><span class="w"> </span><span class="s2">"IRC </span><span class="nv">$count_type</span><span class="s2">"</span>
</code></pre></div>
<p>This script will need to be available from the user which you run MRTG as. I
have placed mine in <code>/usr/local/bin/inspircd-stats.sh</code> but you could place it
somewhere like <code>$HOME/bin/inspircd-stats.sh</code>.</p>
<p>To download and install this to /usr/local/bin run the following:</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>mkdir<span class="w"> </span>-p<span class="w"> </span>/usr/local/bin
sudo<span class="w"> </span>wget<span class="w"> </span>https://raw.github.com/gist/1042604/b19a4cfd91b6956063d962c977f3d5f8e3318d7d/inspircd-stats.sh<span class="w"> </span>-O<span class="w"> </span>/usr/local/bin/inspircd-stats.sh
sudo<span class="w"> </span>chmod+x<span class="w"> </span>/usr/local/bin/inspircd-stats.sh
</code></pre></div>
<p>Now you have this <code>inspircd-stats.sh</code> script, you can use it to get the user
and channel count as follows:</p>
<div class="highlight"><pre><span></span><code>/usr/local/bin/inspircd-stats.sh<span class="w"> </span>user
/usr/local/bin/inspircd-stats.sh<span class="w"> </span>channel
</code></pre></div>
<h3>MRTG Config</h3>
<p>You will need to add the following to your mrtg config file, this could be
located at <code>/etc/mrtg.cfg</code>, but this depends on how you setup mrtg.</p>
<div class="highlight"><pre><span></span><code>#<span class="w"> </span>For<span class="w"> </span>IRC<span class="w"> </span>Users:
Target[irc.users]:<span class="w"> </span>`/usr/local/bin/inspircd-stats.sh<span class="w"> </span>user`
Title[irc.users]:<span class="w"> </span>IRC<span class="w"> </span>Users
PageTop[irc.users]:<span class="w"> </span><span class="nt"><h1></span>IRC<span class="w"> </span>Users<span class="nt"></h1></span>
MaxBytes[irc.users]:<span class="w"> </span>10000000000
ShortLegend[irc.users]:<span class="w"> </span>users
YLegend[irc.users]:<span class="w"> </span>Users
LegendI[irc.users]:<span class="w"> </span>Users
LegendO[irc.users]:
Legend1[irc.users]:<span class="w"> </span>Users
Legend2[irc.users]:
Options[irc.users]:<span class="w"> </span>growright,nopercent,gauge
#<span class="w"> </span>For<span class="w"> </span>IRC<span class="w"> </span>Channels:
Target[irc.channels]:<span class="w"> </span>`/usr/local/bin/inspircd-stats.sh<span class="w"> </span>channel`
Title[irc.channels]:<span class="w"> </span>IRC<span class="w"> </span>Channels
PageTop[irc.channels]:<span class="w"> </span><span class="nt"><h1></span>IRC<span class="w"> </span>Channels<span class="nt"></h1></span>
MaxBytes[irc.channels]:<span class="w"> </span>10000000000
ShortLegend[irc.channels]:<span class="w"> </span>channels
YLegend[irc.channels]:<span class="w"> </span>Channels
LegendI[irc.channels]:<span class="w"> </span>Channels
LegendO[irc.channels]:
Legend1[irc.channels]:<span class="w"> </span>Channels
Legend2[irc.channels]:
Options[irc.channels]:<span class="w"> </span>growright,nopercent,gauge
</code></pre></div>
<p>Now, you can run your MRTG command for your config or wait for the MRTG cron
and it should show up your InspIRCd stats.</p>Pre-populating data in the admin panel2009-07-01T00:00:00+01:002009-07-01T00:00:00+01:00Kyle Fullertag:fuller.li,2009-07-01:/posts/pre-populating-data-admin-panel/<p>I have always found it awkward working with sites and user's in django admin panel. James Bennet from <a href="http://www.b-list.org/weblog/2008/dec/24/admin/">b-list.org</a> explained his way of doing it on his blog, but I found his way a bit limiting, I still want superusers to be able to change the author of a post.</p>
<p>After looking around in the source code for <code>ModelAdmin</code> I found two method's, one of which was not documented. These were <code>formfield_for_manytomany</code> and <code>formfield_for_foreignkey</code>. These methods allow us to supply our own FormField, which could have initial data. These …</p><p>I have always found it awkward working with sites and user's in django admin panel. James Bennet from <a href="http://www.b-list.org/weblog/2008/dec/24/admin/">b-list.org</a> explained his way of doing it on his blog, but I found his way a bit limiting, I still want superusers to be able to change the author of a post.</p>
<p>After looking around in the source code for <code>ModelAdmin</code> I found two method's, one of which was not documented. These were <code>formfield_for_manytomany</code> and <code>formfield_for_foreignkey</code>. These methods allow us to supply our own FormField, which could have initial data. These methods also get passed the <code>HttpRequest</code>, which contains the user. So we can fill in author of a blog post and the current site.</p>
<p>In this post I will quickly show you how to use this method to fill in current data based upon the request inside the admin.</p>
<h3>My model</h3>
<p>Here is a model from which the rest of the article will work from.</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
<span class="kn">from</span> <span class="nn">django.contrib.sites.models</span> <span class="kn">import</span> <span class="n">Site</span>
<span class="kn">from</span> <span class="nn">django.contrib.auth.models</span> <span class="kn">import</span> <span class="n">User</span>
<span class="k">class</span> <span class="nc">Post</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
<span class="n">title</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">255</span><span class="p">)</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">TextField</span><span class="p">(</span><span class="n">blank</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">sites</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ManyToManyField</span><span class="p">(</span><span class="n">Site</span><span class="p">)</span>
<span class="n">author</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="n">User</span><span class="p">)</span>
</code></pre></div>
<p>As you can see, the author is a ForeignKey field, and sites is a ManyToMany field. The methods we use to set the default reflects this. We use <code>formfield_for_foreignkey</code> to set the default user.</p>
<h3>Here is my ModelAdmin class</h3>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">PostAdmin</span><span class="p">(</span><span class="n">admin</span><span class="o">.</span><span class="n">ModelAdmin</span><span class="p">):</span>
<span class="n">fieldsets</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="p">{</span>
<span class="s1">'fields'</span><span class="p">:</span> <span class="p">(</span><span class="s1">'title'</span><span class="p">,</span> <span class="s1">'content'</span><span class="p">,)</span>
<span class="p">}),</span>
<span class="p">(</span><span class="s1">'Advanced options'</span><span class="p">,</span> <span class="p">{</span>
<span class="s1">'classes'</span><span class="p">:</span> <span class="p">(</span><span class="s1">'collapse'</span><span class="p">,),</span>
<span class="s1">'fields'</span><span class="p">:</span> <span class="p">(</span><span class="s1">'author'</span><span class="p">,</span> <span class="s1">'sites'</span><span class="p">,)</span>
<span class="p">})</span>
<span class="p">)</span>
<span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Post</span><span class="p">,</span> <span class="n">PostAdmin</span><span class="p">)</span>
</code></pre></div>
<h3>Selecting the current site</h3>
<p>To select the current site, what we will do is create a method called <code>formfield_for_manytomany</code> inside our <code>PostAdmin</code> class. This method will be called each time the admin panel goes to draw a <code>ManyToMany</code> field in a Post. So all we do is set the initial value to the current site. But it is important to remember that the field may be something else, so it is important to check if it is the sites field.</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">formfield_for_manytomany</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db_field</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">db_field</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s1">'sites'</span><span class="p">:</span>
<span class="n">kwargs</span><span class="p">[</span><span class="s1">'initial'</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">Site</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get_current</span><span class="p">()]</span>
<span class="k">return</span> <span class="n">db_field</span><span class="o">.</span><span class="n">formfield</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">(</span><span class="n">PostAdmin</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">formfield_for_manytomany</span><span class="p">(</span><span class="n">db_field</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</code></pre></div>
<h3>Selecting the current user</h3>
<p>This is very similar to selecting the current site, instead we create a method called <code>formfield_for_foreignkey</code> for ForeignKey's instead of <code>ManyToMany</code> fields.</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">formfield_for_foreignkey</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db_field</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">db_field</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s1">'author'</span><span class="p">:</span>
<span class="n">kwargs</span><span class="p">[</span><span class="s1">'initial'</span><span class="p">]</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">id</span>
<span class="k">return</span> <span class="n">db_field</span><span class="o">.</span><span class="n">formfield</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">(</span><span class="n">PostAdmin</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">formfield_for_foreignkey</span><span class="p">(</span><span class="n">db_field</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</code></pre></div>
<p>Now that we have selected the current user, it would be nice to only let superusers use this field, but I couldn't find a way to only show this for certain users. So instead we will just disallow normal user's from saving the post as another user.</p>
<p>I dont think it is possible to optionally hide the author field from a user, so what I have done was to disallow a non superuser from editing the post's author.</p>
<h4>Disallowing a user from changing another post</h4>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">has_change_permission</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">obj</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">has_class_permission</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">PostAdmin</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">has_change_permission</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">has_class_permission</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">is_superuser</span> <span class="ow">and</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="o">!=</span> <span class="n">obj</span><span class="o">.</span><span class="n">author</span><span class="o">.</span><span class="n">id</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">False</span>
</code></pre></div>
<p>This snippet was not written by myself, but by James Bennet from <a href="http://www.b-list.org/weblog/2008/dec/24/admin/">b-list.org</a>.</p>
<h3>Only let the user view their own posts</h3>
<p>Another measure to limit the possibilities of the user can be to let the user only view their own posts. To do this we just change the queryset to filter posts where the author is themselves. We still want superusers to see all posts, so only filter it for normal users.</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">queryset</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
<span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">is_superuser</span><span class="p">:</span>
<span class="k">return</span> <span class="n">Post</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
<span class="k">return</span> <span class="n">Post</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">author</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">)</span>
</code></pre></div>
<p>To see a complete example of this you can view the source code for <a href="https://github.com/kylef/lithium">lithium</a>.</p>How to install irssi on Dreamhost2009-02-24T00:00:00+00:002009-02-24T00:00:00+00:00Kyle Fullertag:fuller.li,2009-02-24:/posts/how-install-irssi-dreamhost/<p>This was a useful guide I wrote back on my old website. I was trying to install irssi, and I couldn't remember how I installed it last time. Luckily it was available on archive.org's <a href="http://www.archive.org/web/web.php">WayBackMachine</a>. I have updated this guide to glib-2.19.8 and irssi-0.8.15.</p>
<p>To install irssi, you can eigher follow the next 7 steps and install from source.</p>
<p>Every command on this page should be entered into a SSH Prompt on DreamHost</p>
<h3>SSH into your Dreamhost server (for me, this is titan.dreamhost.com …</h3><p>This was a useful guide I wrote back on my old website. I was trying to install irssi, and I couldn't remember how I installed it last time. Luckily it was available on archive.org's <a href="http://www.archive.org/web/web.php">WayBackMachine</a>. I have updated this guide to glib-2.19.8 and irssi-0.8.15.</p>
<p>To install irssi, you can eigher follow the next 7 steps and install from source.</p>
<p>Every command on this page should be entered into a SSH Prompt on DreamHost</p>
<h3>SSH into your Dreamhost server (for me, this is titan.dreamhost.com, you can find your server inside <a href="https://panel.dreamhost.com/">panel</a>)</h3>
<h3>Create the necessary directories:</h3>
<div class="highlight"><pre><span></span><code>mkdir<span class="w"> </span>-p<span class="w"> </span>bin<span class="w"> </span>lib<span class="w"> </span>tmp
chmod<span class="w"> </span><span class="m">700</span><span class="w"> </span>bin<span class="w"> </span>lib<span class="w"> </span>tmp
</code></pre></div>
<h3>Adding lines to your ~/.bash_profile</h3>
<div class="highlight"><pre><span></span><code><span class="nb">echo</span><span class="w"> </span><span class="s2">"export PATH=</span><span class="nv">$PATH</span><span class="s2">:</span><span class="nv">$HOME</span><span class="s2">/bin"</span><span class="w"> </span>>><span class="w"> </span>~/.bash_profile
<span class="nb">echo</span><span class="w"> </span><span class="s2">"export PKG_CONFIG_PATH=</span><span class="nv">$HOME</span><span class="s2">/lib/pkgconfig"</span><span class="w"> </span>>><span class="w"> </span>~/.bash_profile
<span class="nb">echo</span><span class="w"> </span><span class="s2">"export LD_LIBRARY_PATH=</span><span class="nv">$HOME</span><span class="s2">/lib:/usr/local/lib:</span><span class="nv">$LD_LIBRARY_PATH</span><span class="s2">"</span><span class="w"> </span>>><span class="w"> </span>~/.bash_profile
</code></pre></div>
<h3>Activate the new changes</h3>
<div class="highlight"><pre><span></span><code><span class="nb">source</span><span class="w"> </span>~/.bash_profile
</code></pre></div>
<h3>Download, untar, and install glib</h3>
<div class="highlight"><pre><span></span><code><span class="nb">cd</span><span class="w"> </span>~/tmp
wget<span class="w"> </span>ftp://ftp.gtk.org/pub/glib/2.19/glib-2.19.8.tar.gz
tar<span class="w"> </span>-xvzf<span class="w"> </span>glib-2.19.8.tar.gz
<span class="nb">cd</span><span class="w"> </span>glib-2.19.8
./configure<span class="w"> </span>--prefix<span class="o">=</span><span class="nv">$HOME</span>
make
make<span class="w"> </span>install
</code></pre></div>
<h3>Download and install irssi</h3>
<div class="highlight"><pre><span></span><code><span class="nb">cd</span><span class="w"> </span>~/tmp
wget<span class="w"> </span>http://irssi.org/files/irssi-0.8.15.tar.gz
tar<span class="w"> </span>-xvvzf<span class="w"> </span>irssi-0.8.15.tar.gz
<span class="nb">cd</span><span class="w"> </span>irssi-0.8.15
./configure<span class="w"> </span>--prefix<span class="o">=</span><span class="nv">$HOME</span>
make
make<span class="w"> </span>install
</code></pre></div>
<h3>Clean up</h3>
<div class="highlight"><pre><span></span><code>rm<span class="w"> </span>-rf<span class="w"> </span>~/tmp/glib-2.19.8.tar.gz<span class="w"> </span>~/tmp/glib-2.19.8<span class="w"> </span>~/tmp/irssi-0.8.15.tar.gz<span class="w"> </span>~/tmp/irssi-0.8.15
</code></pre></div>
<p>Once you have installed it, simply type <code>irssi</code> to run it.</p>
<p>I asked Dreamhost if they would allow the use of a terminal application running
all the time. Here is their response:</p>
<blockquote>
<p>You won’t be in “trouble” for running an irc program (no servers) on our
servers. However if the system becomes unstable, it would likely be one of
the first processes killed by our admins as our servers are meant for hosting
web pages only. In general , we just don’t want users running bots or their
own server daemons on our servers.”</p>
</blockquote>