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 -&gt; 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>.&#160;<a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">&#8617;</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.&#160;<a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text">&#8617;</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.&#160;<a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text">&#8617;</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.&#160;<a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text">&#8617;</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.&#160;<a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text">&#8617;</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&amp;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.&#160;<a class="footnote-backref" href="#fnref:6" title="Jump back to footnote 6 in the text">&#8617;</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 &quot;kylef/Stencil&quot; ~&gt; 0.6.0 github &quot;kylef/Commander&quot; ~&gt; 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">&amp;&amp;</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">&amp;&amp;</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">&quot;Commander&quot;</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">&quot;Stencil&quot;</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">&#39;CI Automation&#39;</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">&#39;trunk.cocoapods.org&#39;</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">&quot;7.0&quot;</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">&#39;Release 1.0.0&#39;</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 = &quot;com.apple.product-type.bundle&quot;;</span> <span class="gi">+productType = &quot;com.apple.product-type.bundle.unit-test&quot;;</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">&#39;Kiwi/XCTest&#39;</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 &quot;just work&quot; 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 &quot;down&quot; 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">@&quot;Hello World!&quot;</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">@&quot;Completion for %@&quot;</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">@&quot;Completion for %@&quot;</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">&quot;</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">&quot;</span><span class="w"> </span><span class="s2">&quot;CFBundleShortVersionString&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">GIT_RELEASE_VERSION</span><span class="p">#*v</span><span class="si">}</span><span class="s2">&quot;</span> defaults<span class="w"> </span>write<span class="w"> </span><span class="s2">&quot;</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">&quot;</span><span class="w"> </span><span class="s2">&quot;CFBundleVersion&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">COMMITS</span><span class="si">}</span><span class="s2">&quot;</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 &gt; Add Build Phase &gt; 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">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;https://browserid.org/include.js&quot;</span> <span class="na">type</span><span class="o">=</span><span class="s">&quot;text/javascript&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</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">&quot;iss&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;kylefuller.co.uk&quot;</span><span class="p">,</span> <span class="w"> </span><span class="s2">&quot;exp&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;1313971280961&quot;</span><span class="p">,</span> <span class="w"> </span><span class="s2">&quot;public-key&quot;</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">&quot;principal&quot;</span><span class="o">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="s2">&quot;email&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;inbox@kylefuller.co.uk&quot;</span> <span class="w"> </span><span class="p">}</span> <span class="p">}</span> </pre></div> <p>The &quot;iss&quot; 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">&quot;exp&quot;</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">&quot;aud&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;https://demand-browserid.herokuapp.com&quot;</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">&lt;module</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;m_httpd.so&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;module</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;m_httpd_stats.so&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;bind</span><span class="w"> </span><span class="na">address=</span><span class="s">&quot;localhost&quot;</span><span class="w"> </span><span class="na">port=</span><span class="s">&quot;8081&quot;</span><span class="w"> </span><span class="na">type=</span><span class="s">&quot;httpd&quot;</span><span class="w"> </span><span class="na">ssl=</span><span class="s">&quot;gnutls&quot;</span><span class="nt">&gt;</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">&quot;https://localhost:8081/stats&quot;</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">&quot;</span><span class="nv">$count_type</span><span class="s2">&quot;</span><span class="w"> </span>!<span class="o">=</span><span class="w"> </span><span class="s2">&quot;user&quot;</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$count_type</span><span class="s2">&quot;</span><span class="w"> </span>!<span class="o">=</span><span class="w"> </span><span class="s2">&quot;channel&quot;</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">&quot;Usage: </span><span class="nv">$0</span><span class="s2"> &lt;user|channel&gt;&quot;</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">&quot;inspircdstats/general/</span><span class="si">${</span><span class="nv">count_type</span><span class="si">}</span><span class="s2">count&quot;</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">&quot;IRC </span><span class="nv">$count_type</span><span class="s2">&quot;</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">&lt;h1&gt;</span>IRC<span class="w"> </span>Users<span class="nt">&lt;/h1&gt;</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">&lt;h1&gt;</span>IRC<span class="w"> </span>Channels<span class="nt">&lt;/h1&gt;</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">&#39;fields&#39;</span><span class="p">:</span> <span class="p">(</span><span class="s1">&#39;title&#39;</span><span class="p">,</span> <span class="s1">&#39;content&#39;</span><span class="p">,)</span> <span class="p">}),</span> <span class="p">(</span><span class="s1">&#39;Advanced options&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="s1">&#39;classes&#39;</span><span class="p">:</span> <span class="p">(</span><span class="s1">&#39;collapse&#39;</span><span class="p">,),</span> <span class="s1">&#39;fields&#39;</span><span class="p">:</span> <span class="p">(</span><span class="s1">&#39;author&#39;</span><span class="p">,</span> <span class="s1">&#39;sites&#39;</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">&#39;sites&#39;</span><span class="p">:</span> <span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;initial&#39;</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">&#39;author&#39;</span><span class="p">:</span> <span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;initial&#39;</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">&quot;export PATH=</span><span class="nv">$PATH</span><span class="s2">:</span><span class="nv">$HOME</span><span class="s2">/bin&quot;</span><span class="w"> </span>&gt;&gt;<span class="w"> </span>~/.bash_profile <span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;export PKG_CONFIG_PATH=</span><span class="nv">$HOME</span><span class="s2">/lib/pkgconfig&quot;</span><span class="w"> </span>&gt;&gt;<span class="w"> </span>~/.bash_profile <span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;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">&quot;</span><span class="w"> </span>&gt;&gt;<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>