{"version":"https://jsonfeed.org/version/1","title":"Caius Theory","description":"Recent content on Caius Theory","home_page_url":"https://caiustheory.com/","feed_url":"https://caiustheory.com/feed.json","author":{"name":"Caius Durling"},"items":[{"title":"jj jj jj jj jj","date_published":"2025-12-23T10:00:00Z","date_modified":"2025-12-23T10:00:00Z","id":"https://caiustheory.com/jj-jj-jj-jj-jj/","url":"https://caiustheory.com/jj-jj-jj-jj-jj/","content_html":"\u003cp\u003eIn a similar vein to \u003ca href=\"/git-git-git-git-git/\"\u003egit git git git git\u003c/a\u003e, if you\u0026rsquo;re using \u003ca href=\"https://www.jj-vcs.dev/\"\u003ejj\u003c/a\u003e and find you\u0026rsquo;ve accidentally typed one \u003ccode\u003ejj\u003c/code\u003e too many in the command line, it errors at you. In this case we\u0026rsquo;re trying to pull the id of the current changeset we\u0026rsquo;re editing:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ jj jj show -T \u003cspan class=\"s1\"\u003e\u0026#39;change_id.short()\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eerror: unrecognized subcommand \u003cspan class=\"s1\"\u003e\u0026#39;jj\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eUsage: jj \u003cspan class=\"o\"\u003e[\u003c/span\u003eOPTIONS\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u0026lt;COMMAND\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eFor more information, try \u003cspan class=\"s1\"\u003e\u0026#39;--help\u0026#39;\u003c/span\u003e.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHow annoying. Now we could solve this with a shell alias, but \u003ccode\u003ejj\u003c/code\u003e also allows us to define alias\u0026rsquo; in the configuration (much like git!), and combined with \u003ca href=\"https://docs.jj-vcs.dev/latest/cli-reference/#jj-util-exec\"\u003e\u003ccode\u003ejj util exec\u003c/code\u003e\u003c/a\u003e we should be able to have it run the rest of the line nay bother.\u003c/p\u003e\n\u003cp\u003eThinking it through, the initial \u003ccode\u003ejj\u003c/code\u003e will be exec\u0026rsquo;d by the shell, so that\u0026rsquo;s gone already. The second \u003ccode\u003ejj\u003c/code\u003e is then evaluated as the alias, so that\u0026rsquo;s gone too. The remaining arguments passed to the alias are \u003ccode\u003e[\u0026quot;show\u0026quot;, \u0026quot;-T\u0026quot;, \u0026quot;change_id.short()\u0026quot;]\u003c/code\u003e so if we just exec those it\u0026rsquo;ll error trying to run \u003ccode\u003eshow\u003c/code\u003e as a binary. We can avoid that by having it exec jj then the arguments from the alias.\u003c/p\u003e\n\u003cp\u003eDrop the alias in a jj config file (\u003ccode\u003ejj config edit\u003c/code\u003e is super useful for editing these, or there\u0026rsquo;s \u003ccode\u003ejj config  set\u003c/code\u003e to update it from the shell) and then we can test it works.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ealiases\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# jj all the way down\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ejj\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;util\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;exec\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;jj\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLets try our original nested command again and see if we get to see our sweet sweet change id:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ jj jj show -T \u003cspan class=\"s1\"\u003e\u0026#39;change_id.short()\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eerror: unexpected argument \u003cspan class=\"s1\"\u003e\u0026#39;-T\u0026#39;\u003c/span\u003e found\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  tip: to pass \u003cspan class=\"s1\"\u003e\u0026#39;-T\u0026#39;\u003c/span\u003e as a value, use \u003cspan class=\"s1\"\u003e\u0026#39;-- -T\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eUsage: jj util \u003cspan class=\"nb\"\u003eexec\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003eOPTIONS\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u0026lt;COMMAND\u0026gt; \u003cspan class=\"o\"\u003e[\u003c/span\u003eARGS\u003cspan class=\"o\"\u003e]\u003c/span\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eFor more information, try \u003cspan class=\"s1\"\u003e\u0026#39;--help\u0026#39;\u003c/span\u003e.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOh. Our \u003ccode\u003e-T\u003c/code\u003e argument is being interpreted by \u003ccode\u003ejj util exec\u003c/code\u003e, not by the \u003ccode\u003ejj\u003c/code\u003e command we\u0026rsquo;re running. Classic nested argument parsing when you\u0026rsquo;re trying to invoke another command. Luckily shell/exec has a solution to this, passing \u003ccode\u003e--\u003c/code\u003e as an argument stops parsing all remaining arguments so they are passed along verbatim.\u003c/p\u003e\n\u003cp\u003eWith that in mind we can amend our alias to run \u003ccode\u003ejj util exec\u003c/code\u003e and then stop interpreting any further arguments.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ealiases\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# jj all the way down\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ejj\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;util\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;exec\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;--\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;jj\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we should be able to see our change id without any issues, no matter how many nested \u003ccode\u003ejj\u003c/code\u003es there are in the command!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ jj show -T \u003cspan class=\"s1\"\u003e\u0026#39;change_id.short()\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eupvqxuzzvxtx\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ jj jj show -T \u003cspan class=\"s1\"\u003e\u0026#39;change_id.short()\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eupvqxuzzvxtx\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ jj jj jj jj jj show -T \u003cspan class=\"s1\"\u003e\u0026#39;change_id.short()\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eupvqxuzzvxtx\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ jj jj jj jj jj jj jj jj jj jj jj jj jj jj jj jj jj show -T \u003cspan class=\"s1\"\u003e\u0026#39;change_id.short()\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eupvqxuzzvxtx\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHappy committing.\u003c/p\u003e\n"},{"title":"Apple Watch Band Mnemonic","date_published":"2025-07-27T18:57:48+01:00","date_modified":"2025-07-27T18:57:48+01:00","id":"https://caiustheory.com/apple-watch-band-mnemonic/","url":"https://caiustheory.com/apple-watch-band-mnemonic/","content_html":"\u003cp\u003eI\u0026rsquo;ve been wearing an \u003ca href=\"https://www.apple.com/watch/\"\u003eApple Watch\u003c/a\u003e pretty regularly for nearly a decade at this point (October 2015), and have picked up a few bands over time that I swap between. Commonly I\u0026rsquo;ll swap from a fabric based band to a rubber/silicone based band when doing DIY or sports as they seem to stand up better over time to those activities.\u003c/p\u003e\n\u003cp\u003eWithout fail every single time I swap the band I have to think \u003cstrong\u003ereally\u003c/strong\u003e hard which way round the new band is going onto the watch body. For the fabric loops is it the single end or the hooped end going on the top of the body? For the two piece bands is it the short or the long band that goes on the top of the body?\u003c/p\u003e\n\u003cp\u003eI finally figured out a mnemonic to help me remember, and having used it for a few weeks it appears to have lodged in my brain. Pretty simple, it\u0026rsquo;s a single word:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eLongbottom.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eFor the two piece straps the longer one goes on the bottom (and shorter on the top), and for the fabric loops the hoop goes on the bottom because it has the longer piece of the strap running through it. \u003cem\u003e(The latter is more tenuous.)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eTo remember longbottom specifically, the first reference that comes to my mind is Neville Longbottom, who went from being a weed to being pretty awesome in a well known series of books\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e and films. Secondly it\u0026rsquo;s a place in Middle-Earth, where \u003ca href=\"https://lotr.fandom.com/wiki/Pipe-weed\"\u003ePipe-weed\u003c/a\u003e was first grown.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eWith an author who appears to be doing their best to totally alienate any fanbase. Sigh.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"Set Up Rails Activestorage With Azure Securely","date_published":"2024-10-02T22:28:49+01:00","date_modified":"2024-10-02T22:28:49+01:00","id":"https://caiustheory.com/set-up-rails-activestorage-with-azure-securely/","url":"https://caiustheory.com/set-up-rails-activestorage-with-azure-securely/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003ca href=\"https://rubyonrails.org\"\u003eRuby on Rails\u003c/a\u003e has built-in support for managing uploaded files with \u003ca href=\"https://github.com/rails/rails/tree/main/activestorage#readme\"\u003eActiveStorage\u003c/a\u003e, which both cleans up your application code and acts as an abstraction over different storage backends. Azure Storage is one of the supported backends, but configuring it securely can take a little figuring out.\u003c/p\u003e\n\u003cp\u003eThe most sensible way I\u0026rsquo;ve found to have it configured is with files stored having no public access, but allowing temporary access via signed URLs for both uploads \u0026amp; downloads. ActiveStorage happily generates these URLs for us which makes it easy for developers to use and transparent to users, whilst keeping their data as secure and protected as possible. From a technical point of view, this also allows upload and download between Azure Storage and the user\u0026rsquo;s browser, without proxying all the data through your Rails app.\u003c/p\u003e\n\u003ch3 id=\"azure-setup\"\u003eAzure setup\u003c/h3\u003e\n\u003cp\u003eWe\u0026rsquo;ll be storing the files in Azure as blobs within \u003ca href=\"https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction\"\u003eAzure Blob Storage\u003c/a\u003e. Blobs live within a Storage Container which lives within a Storage Account, which is created in a specific geographical region and Resource Group.\u003c/p\u003e\n\u003cp\u003eTo create the above, we\u0026rsquo;ll need access to an Azure Subscription with permissions to create the resources. (Most of the time you will already be an admin on the subscription, so can ignore checking this. If you get permission errors creating the below, you\u0026rsquo;ll need to go talk to your admin.)\u003c/p\u003e\n\u003cp\u003eMost resources in Azure need to live within a Resource Group, which it\u0026rsquo;s suggested you use to group related resources together. If there isn\u0026rsquo;t already a resource group for your application resources, create one.\u003c/p\u003e\n\u003cp\u003eNext we need to create a Storage Account in the main location for the app and nested in the Resource Group from above. The following settings are split across multiple pages, click \u0026ldquo;Next\u0026rdquo; to advance to next bit of the form:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eStorage account name\u003c/strong\u003e: 3-24 numbers/lowercase letters only, unique across all storage accounts globally in Azure. Good luck. (eg, \u003ccode\u003est123someappproduksth\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRegion\u003c/strong\u003e: choose for your app location (eg, UK South)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePerformance\u003c/strong\u003e: Standard (general purpose v2 account)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRedundancy\u003c/strong\u003e: Zone-redundant storage (ZRS) if possible. (Some regions don\u0026rsquo;t support it, choose Locally-redundant storage (LRS) in those.)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEnable infrastructure encryption\u003c/strong\u003e: Check the box, it\u0026rsquo;s free.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTags\u003c/strong\u003e: tag the resource according to your internal conventions/policies (eg, \u003ccode\u003eterraform:false\u003c/code\u003e)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eCreate the Storage Account and wait for it to complete. Next we\u0026rsquo;ll create the Storage Container nested under the Storage Account which is where our actual files will be uploaded to. Find the Storage Account we created in the portal and click \u0026ldquo;Containers\u0026rdquo; in the sidebar, then \u0026ldquo;+ Container\u0026rdquo; above the table listing the containers.\u003c/p\u003e\n\u003cp\u003eAdd a sensible name for the Container, which is easier because there are \u003ca href=\"https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names\"\u003efewer restrictions on this name\u003c/a\u003e. (eg, \u003ccode\u003edragon-myapp-production-uksouth-activestorage\u003c/code\u003e.) Create the container and wait for that to complete.\u003c/p\u003e\n\u003cp\u003eNavigate back to viewing the Storage Account and select \u0026ldquo;Access Keys\u0026rdquo; in the sidebar. Make a note of the Key value (for key1) for use later configuring Rails. If you need to rotate the access keys later without causing an outage, you can update the app to use the Key from key2 and then rotate the key for key1 from the UI.\u003c/p\u003e\n\u003cp\u003eThe final configuration we might need to set in Azure is allowing direct uploads from our app to the container. Due to this being client-side requests we need the Cross-Origin Resource Sharing (CORS) headers to allow this from Azure\u0026rsquo;s side. Without these in place you will get permissions errors from browsers trying to make requests directly to Azure. If you\u0026rsquo;re not using ActiveStorage\u0026rsquo;s Direct Upload feature, you can skip this.\u003c/p\u003e\n\u003cp\u003eFind \u0026ldquo;Resource Sharing (CORS)\u0026rdquo; in the sidebar for the Storage Account. Add a new entry to the \u0026ldquo;Blob service\u0026rdquo; table:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eAllowed origins: comma-separated list of all your app domains using this storage.\u003c/li\u003e\n\u003cli\u003eAllowed methods: \u003ccode\u003eGET\u003c/code\u003e, \u003ccode\u003ePOST\u003c/code\u003e, \u003ccode\u003eOPTIONS\u003c/code\u003e, \u003ccode\u003ePUT\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eAllowed headers: *\u003c/li\u003e\n\u003cli\u003eExposed headers: *\u003c/li\u003e\n\u003cli\u003eMax age: 0\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eClick out of the fields and the configuration will be saved automagically. (Little green ticks appear in the fields for some visual feedback.)\u003c/p\u003e\n\u003ch3 id=\"rails-setup\"\u003eRails setup\u003c/h3\u003e\n\u003cp\u003eWe can follow the Rails documentation for \u003ca href=\"https://guides.rubyonrails.org/active_storage_overview.html#setup\"\u003esetting up ActiveStorage\u003c/a\u003e, ensures you have the migrations for the required database tables installed and the framework is loaded in your application.\u003c/p\u003e\n\u003cp\u003eFollow the rails docs \u003ca href=\"https://guides.rubyonrails.org/active_storage_overview.html#microsoft-azure-storage-service\"\u003efor configuring \u003ccode\u003econfig/storage.yml\u003c/code\u003e with Azure\u003c/a\u003e, and pulling \u003ccode\u003eazure-storage-blob\u003c/code\u003e into your app. (In Rails 8.1 this will be \u003ccode\u003eazure-blob\u003c/code\u003e instead, see \u003ca href=\"https://testdouble.com/insights/azure-blob-a-new-ruby-gem-for-azure-blob-storage\"\u003etest double\u0026rsquo;s post on the subject for more information\u003c/a\u003e) I suggest storing the key in Rails credentials (or however you inject secrets into your rails app), and creating a new block in \u003ccode\u003estorage.yml\u003c/code\u003e for Azure storage. Using credentials per-environment makes using a different storage account for Staging \u0026amp; Production easy.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eazure_storage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;AzureStorage\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003estorage_account_name\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;%= Rails.application.credentials.dig(:azure_storage, :storage_account_name) %\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003estorage_access_key\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003econtainer\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;%= Rails.application.credentials.dig(:azure_storage, :container_name) %\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEdit the credentials for the given environment (eg, \u003ccode\u003ebin/rails credentials:edit --environment=production\u003c/code\u003e) and enter the values we got from Azure above:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eazure_storage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003estorage_account_name\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;st123someappproduksth\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003estorage_access_key\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Qm9yZWQgeWV0PyBGdWNraW5nIGJhbGxhY2hlIGlubml0Lgo=\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003econtainer_name\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;dragon-myapp-production-uksouth-activestorage\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUpdate the appropriate \u003ccode\u003econfig/environments/*.rb\u003c/code\u003e file for the environments you want to use this Azure storage, setting \u003ccode\u003econfig.active_storage.service\u003c/code\u003e to the key of the block in \u003ccode\u003estorage.yml\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eactive_storage\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eservice\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"ss\"\u003e:azure_storage\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDeploy your changes, and you\u0026rsquo;re good to start using ActiveStorage to manage your uploaded files. Ensure the ActiveStorage JS is added to your frontend (depends on which asset pipeline flavour the app is configured to use, should be there by default) and you won\u0026rsquo;t be sending all files through your Rails backend, saves you some bandwidth and CPU cycles.\u003c/p\u003e\n"},{"title":"Programatically invoke rake task with --trace enabled","date_published":"2024-10-01T22:42:00Z","date_modified":"2024-10-01T22:42:00Z","id":"https://caiustheory.com/programatically-invoke-rake-task-with--trace-enabled/","url":"https://caiustheory.com/programatically-invoke-rake-task-with--trace-enabled/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003ca href=\"https://ruby.github.io/rake/\"\u003eRake\u003c/a\u003e is a Make-like program implemented in Ruby \u003cem\u003e(to quote the website.)\u003c/em\u003e It contains tasks written in ruby code you can invoke from the command line. These can run prequisite tasks first too, in this case we configure the \u003ccode\u003esetup\u003c/code\u003e task to be invoked before the \u003ccode\u003ework\u003c/code\u003e task is ever invoked.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003etask\u003c/span\u003e \u003cspan class=\"ss\"\u003e:setup\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Setting up\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003etask\u003c/span\u003e \u003cspan class=\"ss\"\u003ework\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"ss\"\u003e:setup\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Doing work\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ rake work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSetting up\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eDoing work\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRake also supports \u003ccode\u003e--trace\u003c/code\u003e as an argument which outputs a bunch of debugging information, allowing you to see which tasks have been executed in which order and how often they were invoked.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ rake --trace work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke work \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke setup \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSetting up\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eDoing work\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom the \u003ccode\u003e--trace\u003c/code\u003e output we can see calling the same task multiple times in the same rake process doesn\u0026rsquo;t execute the task multiple times, but it is invoked the correct number of times.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ rake --trace work setup work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke work \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke setup \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSetting up\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eDoing work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke work\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor performance reasons (\u003ca href=\"https://rubyonrails.org\"\u003eRails\u003c/a\u003e apps can take multiple seconds to start a new process), we might want to run multiple tasks within the same \u003ccode\u003erake\u003c/code\u003e process.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003etask\u003c/span\u003e \u003cspan class=\"ss\"\u003e:create_assets\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Created some assets\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;re now stuck in a catch-22 from the shell in terms of tracing however, if we run both tasks in the same process we can only trace both, or trace neither.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ rake --trace create_assets work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke create_assets \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute create_assets\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCreated some assets\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke work \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke setup \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSetting up\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eDoing work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ rake create_assets work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCreated some assets\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSetting up\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eDoing work\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we\u0026rsquo;ve previously been calling multiple tasks in different commands and only tracing some of them, we want to maintain the same output in our CI system logs but also reap the performance benefit of not booting the app multiple times over. This means having tracing enabled for some tasks, but not others which we can\u0026rsquo;t do from the shell.\u003c/p\u003e\n\u003cp\u003eLuckily there\u0026rsquo;s another mechanism to invoke multiple rake tasks, we define a new rake task that can then call the other rake tasks and setup tracing before calling the last one by \u003ca href=\"https://github.com/ruby/rake/blob/4538838a4b9d2cbfa1e231716a2183e65241b52e/lib/rake/application.rb#L613-L622\"\u003emimicking the internals of \u003ccode\u003erake --trace\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e(\u003ccode\u003eRake::Task.[]\u003c/code\u003e lets you find rake tasks by their labels, and then \u003ccode\u003eRake::Task#invoke\u003c/code\u003e calls the code defined for the task as if you\u0026rsquo;d run \u003ccode\u003erake whatever\u003c/code\u003e on the cli.)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003etask\u003c/span\u003e \u003cspan class=\"ss\"\u003e:ci_perform\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eRake\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003eTask\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;create_assets\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003einvoke\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e# Enable `--trace` programatically for remaining tasks invoked\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eRake\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eapplication\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etrace\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eRake\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eapplication\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebacktrace\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eRake\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eapplication\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etrace_output\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"vg\"\u003e$stderr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eRake\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003everbose\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eRake\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003eTask\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;work\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003einvoke\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEt voila, we get our trace output for the \u003ccode\u003ework\u003c/code\u003e task but not until that\u0026rsquo;s invoked.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ rake ci_perform\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCreated some assets\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke work \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Invoke setup \u003cspan class=\"o\"\u003e(\u003c/span\u003efirst_time\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSetting up\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e** Execute work\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eDoing work\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"Running OmniOS via UTM on Apple Silicon","date_published":"2024-01-05T01:00:00Z","date_modified":"2024-01-05T01:00:00Z","id":"https://caiustheory.com/running-omnios-via-utm-on-apple-silicon/","url":"https://caiustheory.com/running-omnios-via-utm-on-apple-silicon/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eWanted to fire up \u003ca href=\"https://omnios.org/\"\u003eOmniOS\u003c/a\u003e to play with and didn\u0026rsquo;t have any spare x86 hardware to hand so decided to figure out running it in \u003ca href=\"https://getutm.app/\"\u003eUTM\u003c/a\u003e on Apple Silicon, meaning it needs emulation as OmniOS is x86 only.\u003c/p\u003e\n\u003cp\u003eGrab the ISO from the \u003ca href=\"https://omnios.org/download.html\"\u003eOmniOS downloads page\u003c/a\u003e, I used the LTS release but the current stable should work too. Just make sure it\u0026rsquo;s the ISO version, not USB, etc.\u003c/p\u003e\n\u003cp\u003eOpen UTM and click \u0026ldquo;Create a New Virtual Machine\u0026rdquo;. Choose \u0026ldquo;Emulate\u0026rdquo; as we need a x86 VM on an aarch64 host. Choose \u0026ldquo;Other\u0026rdquo; for the Operating System, then select the iso file you downloaded above from the Browse… button.\u003c/p\u003e\n\u003cp\u003eMake sure to leave the Hardware Architecture set to x86_64, but adjust the rest to what you require. OmniOS suggests 1GB ram minimum for VMs. Storage is suggested to be 8GB minimum.\u003c/p\u003e\n\u003cp\u003eOn the Summary screen check the \u0026ldquo;Open VM Settings\u0026rdquo; and click Save.\u003c/p\u003e\n\u003cp\u003eOn the Settings popover that appears, select Display from the sidebar and change the \u0026ldquo;Emulated Display Card\u0026rdquo; to \u0026ldquo;VGA\u0026rdquo;. This ensures we don\u0026rsquo;t get a black screen when booting.\u003c/p\u003e\n\u003cp\u003eI then chose to make the VM appear as a separate host on my LAN, by changing \u0026ldquo;Network\u0026rdquo; -\u0026gt; \u0026ldquo;Network Mode\u0026rdquo; to \u0026ldquo;Bridged (Advanced)\u0026rdquo;. This isn\u0026rsquo;t required.\u003c/p\u003e\n\u003cp\u003eSave the settings and boot the VM, now you can follow the \u003ca href=\"https://omnios.org/setup/freshinstall.html\"\u003eOmniOS Installer Walk-Through\u003c/a\u003e as normal.\u003c/p\u003e\n\u003cp\u003eOn the post-installation menu I chose \u0026ldquo;\u003cstrong\u003eC\u003c/strong\u003eonfigure the installed OmniOS system\u0026rdquo;, then \u0026ldquo;\u003cstrong\u003eC\u003c/strong\u003eonfigure Networking\u0026rdquo; and changed it from Static to DHCP, so it will pick up an IP at boot from my router.\u003c/p\u003e\n\u003cp\u003eReboot and Enjoy.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/2024-01-06-omnios-running-utm.png\" alt=\"OmniOS boot menu inside a UTM window on a Mac\"\u003e\u003c/p\u003e\n"},{"title":"Upgraded Dekoni Earpads for Sony WH-1000XM3 Headphones","date_published":"2023-12-24T15:09:00Z","date_modified":"2023-12-24T15:09:00Z","id":"https://caiustheory.com/upgraded-dekoni-earpads-for-sony-wh-1000xm3-headphones/","url":"https://caiustheory.com/upgraded-dekoni-earpads-for-sony-wh-1000xm3-headphones/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eBack in 2019 I picked up a pair of \u003ca href=\"https://www.sony.co.uk/electronics/headband-headphones/wh-1000xm3\"\u003eSony WH-1000XM3 Headphones\u003c/a\u003e for using in the office. Very useful and work well in an office and on public transport. After a while I was finding my ears were getting quite hot when wearing them all day, especially on hotter days in the summer.\u003c/p\u003e\n\u003cp\u003eThe original earpads had a shiny synthetic surface, after some research I happened across Dekoni\u0026rsquo;s Synthetic Suede Earpads as a suggested upgrade for the XM3\u0026rsquo;s. Discovered \u003ca href=\"https://www.electromod.co.uk/\"\u003eElectromod\u003c/a\u003e are a UK business that imports them for sale here, and \u003ca href=\"https://www.electromod.co.uk/dekoni-choice-synthetic-earpads-for-sony-wh1000xm3-headphones\"\u003eordered them\u003c/a\u003e. Arrived promptly, and an unrelated issue around payment was easily resolved by customer services. Would buy from them again without any worries.\u003c/p\u003e\n\u003cp\u003eFitting the earpads was pretty simple, the old pads lever off and the new pads are pressed into place. From memory I used a plastic spudger to pop the old pads off. The Synthetic Suede seals much better against my head, is cooler to the touch and doesn\u0026rsquo;t overheat in the same way. I\u0026rsquo;ve taken to wiping them off with a soft cloth occasionally to stop muck building up on them.\u003c/p\u003e\n\u003cp\u003eElectromod also carry upgraded earpads for \u003ca href=\"https://www.electromod.co.uk/dekoni?category=earpads\"\u003emany other headphones\u003c/a\u003e, including Sony XM4/XM5 and Bose QC/700\u0026rsquo;s.\u003c/p\u003e\n"},{"title":"`asdf install ruby` on macOS","date_published":"2023-05-10T21:03:53Z","date_modified":"2023-05-10T21:03:53Z","id":"https://caiustheory.com/asdf-install-ruby-macos/","url":"https://caiustheory.com/asdf-install-ruby-macos/","content_html":"\u003cp\u003eInstalling ruby through \u003ca href=\"https://asdf-vm.com\"\u003easdf\u003c/a\u003e on Apple Silicon Macs\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e will attempt to build a custom OpenSSL for each install because it can\u0026rsquo;t find OpenSSL from homebrew in \u003ccode\u003e/usr/local\u003c/code\u003e, as that\u0026rsquo;s now installed in \u003ccode\u003e/opt/homebrew\u003c/code\u003e. This means your \u003ccode\u003ebrew update\u003c/code\u003e no longer pulls in security fixes for your ruby runtimes, as well as wasting disk space.\u003c/p\u003e\n\u003cp\u003eRuby 2.6.10 \u0026amp; 2.7.x need OpenSSL 1.1, and are unsupported at time of publishing so you should really upgrade to ruby 3! (Tested with ruby 2.6.10 and 2.7.8 at time of publishing.)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebrew install openssl@1.1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eRUBY_CONFIGURE_OPTS\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;--with-openssl-dir=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ebrew --prefix openssl@1.1\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  asdf install ruby 2.7.8\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRuby 3.0 and higher need OpenSSL 3.0+ so we follow the same override but with a different brew name. (Tested with ruby 3.1.4 and 3.2.2 at time of publishing.)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebrew install openssl@3\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eRUBY_CONFIGURE_OPTS\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;--with-openssl-dir=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ebrew --prefix openssl@3\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  asdf install ruby 3.2.2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAlso if you\u0026rsquo;re trying to install a version of ruby that exists in \u003ca href=\"https://github.com/rbenv/ruby-build\"\u003erbenv/ruby-build\u003c/a\u003e \u003ccode\u003emaster\u003c/code\u003e branch, but not in the version of ruby-build \u003ca href=\"https://github.com/asdf-vm/asdf-ruby/blob/master/lib/utils.sh#L3\"\u003easdf-ruby plugin uses\u003c/a\u003e you can override it with \u003ccode\u003eASDF_RUBY_BUILD_VERSION=master\u003c/code\u003e when running \u003ccode\u003easdf ruby install x.y.z\u003c/code\u003e. Pass as an extra envariable to the above commands.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eM1/M2 processors\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"@fediverse@twitter via ///","date_published":"2022-11-04T22:47:00Z","date_modified":"2022-11-04T22:47:00Z","id":"https://caiustheory.com/define.rental.yoga/","url":"https://caiustheory.com/define.rental.yoga/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eAn amusing idea from \u003ca href=\"https://mastodon.xyz/@xor\"\u003e\u003ccode\u003e@xor@mastodon.xyz\u003c/code\u003e\u003c/a\u003e for Mastodon instance names (which form part of your username) has been taking three word phrases from Moby Dick and trying to find available domain names, so you could host your instance at \u003ccode\u003efamous.whaling.house\u003c/code\u003e for example.\u003c/p\u003e\n\u003cp\u003eParker has since \u003ca href=\"https://parkerhiggins.net/2022/11/public-sub-domains\"\u003eblogged his idea and some examples\u003c/a\u003e which is well worth a read and explains it in more depth, as well as explaining how he did and linking to the script if you want a go.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eMany Mastodon instances are on subdomains, and since the early days weirder new-style TLDs have been de rigueur. (The flagship has always been at a .social!) So I set out to find three-word phrases where the third word is a 4+-letter top-level domain, using as my first source text Moby Dick.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eRe-reading through the thread on mastodon again, I spotted the following reply from \u003ca href=\"https://mastodon.social/@knowtheory\"\u003e\u003ccode\u003e@knowtheory@mastodon.social\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eOk. Now map them on what three words\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e— \u003ca href=\"https://mastodon.social/@knowtheory/109282602341495412\"\u003ehttps://mastodon.social/@knowtheory/109282602341495412\u003c/a\u003e\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eNow whilst I don\u0026rsquo;t think what3words is beneficial in their current form\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e, they are definitely a great service for comedy purposes. Given they also have three word phrases, and can point to a location anywhere in the world… what\u0026rsquo;s the funniest location we could put a Mastodon instance on and join the fediverse with?\u003c/p\u003e\n\u003cp\u003eSay, what\u0026rsquo;s the Twitter HQ location on W3W? Searching for \u0026ldquo;Twitter\u0026rdquo; suggests \u0026ldquo;Twitter HQ, Market Street, San Francisco\u0026rdquo;, clicking that shows Twitter\u0026rsquo;s W3W location as \u003ca href=\"https://what3words.com/define.rental.yoga\"\u003e\u003ccode\u003e///define.rental.yoga\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\n\n\u003cimg src=\"/2022-11-04-w3w-define-rental-yoga.png\" alt=\"Screenshot of what3words map showing Twitter HQ at define.rental.yoga\" class=\"center\"\u003e\n\n\n\u003cp\u003eA quick search on the Wikipedia page for \u003ca href=\"https://en.wikipedia.org/wiki/List_of_Internet_top-level_domains\"\u003eInternet Top Level Domains\u003c/a\u003e reveals that \u003ccode\u003e.yoga\u003c/code\u003e is a valid TLD. Further searching on a couple of domain registrars shows that \u003ccode\u003erental.yoga\u003c/code\u003e is also available! For a mere £32/year, and hosting a mastodon instance there you could be \u003ccode\u003e@you@define.rental.yoga\u003c/code\u003e and declare that you haven\u0026rsquo;t left Twitter!\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eFor some reason this amused me enough to blog about it. I have not registered \u003ccode\u003erental.yoga\u003c/code\u003e. Who will? 🙃\u003c/em\u003e\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003ewhat3words has issues when it comes to critical safety scenarios, when it involves humans reading the phrases to each other. See \u003ca href=\"https://cybergibbons.com/security-2/why-what3words-is-not-suitable-for-safety-critical-applications/\"\u003ehttps://cybergibbons.com/security-2/why-what3words-is-not-suitable-for-safety-critical-applications/\u003c/a\u003e for more information. And yet they appear to pay Emergency Services to use and advertise their proprietary service. 🤨\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"iLO 4 Firmware Upgrade","date_published":"2022-07-02T11:01:01Z","date_modified":"2022-07-02T11:01:01Z","id":"https://caiustheory.com/ilo-4-firmware-upgrade/","url":"https://caiustheory.com/ilo-4-firmware-upgrade/","content_html":"\u003cp\u003eHP Microserver Gen8 machines have a \u003ca href=\"https://en.wikipedia.org/wiki/HP_Integrated_Lights-Out\"\u003eHP iLo\u003c/a\u003e 4 built in for remote hands management. These are effectively a separate, embedded computer inside the server\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e, which also means it has its own software (firmware) running on it and needs updating separately.\u003c/p\u003e\n\u003cp\u003eHP are still releasing firmware updates for the iLo 4, and whilst it\u0026rsquo;s possible to update them from inside the host OS on the server, you can also do it by uploading the firmware directly to the iLO. I prefer this method as my servers are almost never running the correct operating system to update from the host\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e.\u003c/p\u003e\n\u003cp\u003eThe easiest way to get ahold of the firmware is to extract it from the Red Hat linux host package, we\u0026rsquo;re after a \u003ccode\u003e.bin\u003c/code\u003e file inside the \u003ccode\u003e.rpm\u003c/code\u003e package HP make available for downloading.\u003c/p\u003e\n\u003cp\u003eAt the time of writing the latest firmware release is v2.8.0, released 2022-04-08, available \u003ca href=\"https://support.hpe.com/connect/s/softwaredetails?language=en_US\u0026amp;softwareId=MTX_d08e4968119e4737b8549928c2\"\u003eon HP\u0026rsquo;s support site here\u003c/a\u003e (no login required). Click \u0026ldquo;View Download Files (2)\u0026rdquo; and then pick the one ending in \u003ccode\u003e.rpm\u003c/code\u003e (\u003ccode\u003efirmware-ilo4-2.80-1.1.i386.rpm\u003c/code\u003e at time of writing.)\u003c/p\u003e\n\u003cp\u003eOnce downloaded, we can unpack the \u003ccode\u003erpm\u003c/code\u003e using the \u003ccode\u003erpm2cpio\u003c/code\u003e tool and then \u003ccode\u003ecpio\u003c/code\u003e to output the files on disk for us.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003erpm2cpio firmware-ilo4-2.80-1.1.i386.rpm \u003cspan class=\"p\"\u003e|\u003c/span\u003e clio -idmv\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe file we\u0026rsquo;re after is nested inside a few directories in the unpacked directory. You can find it under \u003ccode\u003eusr/lib/i386-linux-gnu/firmware-ilo4-2.80-1.1/\u003c/code\u003e named \u003ccode\u003eilo4_280.bin\u003c/code\u003e (at time of writing. Version numbers might differ in future.)\u003c/p\u003e\n\u003cp\u003eOnce you have that \u003ccode\u003ebin\u003c/code\u003e file on disk, go to your iLO web interface and login. Navigate to \u0026ldquo;Administration\u0026rdquo; in the sidebar, then select the \u0026ldquo;Firmware\u0026rdquo; tab. Pick the \u003ccode\u003ebin\u003c/code\u003e file from the file picker and click Upload.\u003c/p\u003e\n\u003cp\u003eWait for flashing to complete and the iLO to restart. If you\u0026rsquo;ve upgraded from \u0026lt; 2.78 then you\u0026rsquo;ll get a new UI as part of the upgrade which looks better and works just as well as the old one. It also adds new functionality, like a HTML5 remote console rather than having to download a \u003ccode\u003e.jar\u003c/code\u003e file to take remote control of the machine.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eOut the box at least. The iLo can be configured to share eth0 with the host instead I believe.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eI think both Windows and Red Hat linux are supported for this.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"Shawbury 10km 2021","date_published":"2021-09-25T16:05:00Z","date_modified":"2021-09-25T16:05:00Z","id":"https://caiustheory.com/shawbury-10km-2021/","url":"https://caiustheory.com/shawbury-10km-2021/","content_html":"\u003cp\u003eRAF Shawbury host an annual 10km running race, and I entered this time because a friend suggested it as an event to aim for over the summer. Given the run is along runways/taxiways it\u0026rsquo;s going to be one of the flattest courses on offer. Seemed like a good idea at the time.\u003c/p\u003e\n\u003cp\u003eGot there with plenty of time to register, figure out where the important things—coffee \u0026amp; ice cream vans—were and go for a nervous wee. Decided to start a workout on my watch early, set it to 10(km, I thought) and paused it straight away to wait for the gun. Stood around for another ~5 minutes waiting for the gun to go. Unpaused the watch as I went across the line and we were off.\u003c/p\u003e\n\u003cp\u003eStarted near the front of the midfield and set out at a pace that I thought was around 10:20/mile, didn\u0026rsquo;t pay too much attention to people coming past me as it was a mass start rather than paced start. Kept pace with some absolute legend running in an Elephant costume (I\u0026rsquo;d overheard they were targeting a 56 minute finish, something I later realised to be a joke.) Easy enough route, round a taxiway and down the main runway to start.\u003c/p\u003e\n\u003cp\u003eMile in, feeling good, lots of energy. Watch buzzes the mile pace … 10:55/mile. Yeesh, that\u0026rsquo;s way slower than I meant to start, and the rolling mile display hadn\u0026rsquo;t displayed anything until I\u0026rsquo;d hit the first mile mark. Oops, time to turn the pace up slightly. Kept going until the rolling mile showed under 10:00/mile, second mile came in at 9:27/mile and then tried to keep it under 10:00/mile whilst not blowing the heart rate up too much.\u003c/p\u003e\n\u003cp\u003eMarshals around the course were lovely and supportive, although compared to a road 10km through town where people come out of their houses and cheer it felt quite quiet as an event. Seeing someone every time a corner occurred was a welcome sight though, and the water stop was quite well placed just before the halfway mark.\u003c/p\u003e\n\u003cp\u003eHit 3.1 miles in around 31-32 minutes which was on pace, legs were feeling okay and I took some water on board whilst running. Second half of the course I started using other people to pace myself, I\u0026rsquo;d pick someone 50-100 meters ahead who didn\u0026rsquo;t appear to be vanishing from me and then try and slowly overhaul them without increasing the heart rate too much.\u003c/p\u003e\n\u003cp\u003eAround the 5 mile mark (~50 minutes) I wondered if I could be on for an hour finish. Also realised as the watch buzzed \u0026ldquo;Halfway there!\u0026rdquo; that it was set to a 10 mile run. Useful. Heart rate was creeping up to maximum, legs were starting to ache and the ball of my left foot was starting to complain about repeatedly slamming into the tarmac. Tried elongating my stride and springing off each step a bit more consciously, also hit the point of talking to myself which is a good sign I\u0026rsquo;m starting to tire and need to start concentrating on what I\u0026rsquo;m doing form-wise.\u003c/p\u003e\n\u003cp\u003eSaw the 1km board, then the 500m board and was looking for a 250m board to kick harder from. Sailed past a friend who had already finished about the 200m mark and was really trying not to blow the heart rate through the roof. Found a 150m board—complete with amusingly sarcastic comment along the lines of \u0026ldquo;ooh, time to put some effort in\u0026rdquo;—and ramped the pace up slightly. Mindful of keeping the heart rate under control still, pushed a little too high according to my stats afterwards but felt okay doing so for a few seconds.\u003c/p\u003e\n\u003cp\u003eCrossed the line with a gun time of 1:00:51. Super chuffed I nearly dipped under the 1 hour mark, expected to be around 1:04 having done a 1:06:30 cross-country 10km earlier in the summer. I \u003cstrong\u003ereally\u003c/strong\u003e must remember not to mess around with my equipment before events though, only make changes before going for training runs because something is \u003cem\u003ealways\u003c/em\u003e setup wrong.\u003c/p\u003e\n\u003cp\u003eIn this case I\u0026rsquo;d switched from kilometres to miles for running events, reset my watch yesterday so it had no music/podcasts on to listen to and also changed the display for a running workout, crucially removing the average pace of the whole activity. What I hadn\u0026rsquo;t foreseen making those changes was I\u0026rsquo;d have to wait a mile before I got \u003cem\u003eany\u003c/em\u003e pace feedback from the watch.\u003c/p\u003e\n\u003cp\u003eI also discovered uploading straight to \u003ca href=\"https://www.strava.com/athletes/2295429\"\u003eStrava\u003c/a\u003e (through \u003ca href=\"https://www.rungap.com\"\u003eRungap\u003c/a\u003e) that pausing the watch ahead of time completely screws up the timings. Strava reckoned I\u0026rsquo;d done an 18:25/mile first mile! I knew it was slow, but it wasn\u0026rsquo;t \u003cem\u003ethat\u003c/em\u003e slow. Ended up exporting a GPX and removing the first 6 minutes stood around with it paused to make Strava happy about the timings.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.strava.com/activities/6016842101\"\u003eRace on Strava\u003c/a\u003e\u003c/p\u003e\n"},{"title":"Fix Edgerouter DHCP ? entries","date_published":"2021-03-14T15:55:00Z","date_modified":"2021-03-14T15:55:00Z","id":"https://caiustheory.com/fix-edgerouter-dhcp-entries/","url":"https://caiustheory.com/fix-edgerouter-dhcp-entries/","content_html":"\u003cp\u003eOccasionally I end up with devices on the local network that don\u0026rsquo;t emit their hostname over DHCP, so when listing the current leases on the EdgeRouter\u0026rsquo;s cli, they just appear as \u0026ldquo;?\u0026rdquo;.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e These usually just irritate me, but occasionally when I\u0026rsquo;m looking for a machine on the network it means I can\u0026rsquo;t find it and end up poking the different \u0026ldquo;?\u0026rdquo; IPs using \u003ccode\u003enmap\u003c/code\u003e or \u003ccode\u003essh\u003c/code\u003e to discover which machines they are.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ show dhcp leases \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eIP address      Hardware Address   Lease expiration     Pool       Client Name\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e----------      ----------------   ----------------     ----       -----------\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e10.0.0.34       a8:1d:16:74:xx:yy  2021/03/14 16:38:27  trusted    ?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e10.0.0.40       b8:27:eb:c5:xx:yy  2021/03/14 16:36:13  trusted    picontrol1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e10.0.0.46       a8:1d:16:75:xx:yy  2021/03/14 16:37:48  trusted    ?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e10.0.0.93       14:f6:d8:53:xx:yy  2021/03/14 16:40:29  trusted    ?\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe EdgeRouter lets me assign static entries in the DHCP subnet, which solves the problem of knowing which hostnames they are, but also pins those devices to (effectively) static IPs within the subnet which leads to me having to know which IPs are free when I assign them, etc. Avoiding that is why I have DHCP on the local network.\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eProvided the EdgeRouter is configured to use \u003ca href=\"https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html\"\u003ednsmasq\u003c/a\u003e to provide DHCP services\u003csup id=\"fnref:3\"\u003e\u003ca href=\"#fn:3\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e3\u003c/a\u003e\u003c/sup\u003e, you can lean on the \u003ccode\u003edhcp-host\u003c/code\u003e option in the dnsmasq configuration to assign a hostname based on MAC address, without prescribing a specific IP address for the machine. This solves the issue of \u0026ldquo;?\u0026rdquo; devices showing up in \u003ccode\u003eshow dhcp leases\u003c/code\u003e, whilst also allowing dynamic IP assignment.\u003c/p\u003e\n\u003cp\u003eYou\u0026rsquo;ll need to know the MAC address in question, and pick a hostname to be assigned to the machine. You\u0026rsquo;ll then want to inject these through dnsmasq\u0026rsquo;s configuration file, which \u003ccode\u003eset service dns forwarding options xxx\u003c/code\u003e nicely injects into on the EdgeRouter.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ configure\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eset\u003c/span\u003e service dns forwarding options \u003cspan class=\"s2\"\u003e\u0026#34;dhcp-host=14:f6:d8:53:xx:yy,cb1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eset\u003c/span\u003e service dns forwarding options \u003cspan class=\"s2\"\u003e\u0026#34;dhcp-host=a8:1d:16:75:xx:yy,cb3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eset\u003c/span\u003e service dns forwarding options \u003cspan class=\"s2\"\u003e\u0026#34;dhcp-host=a8:1d:16:74:xx:yy,cb2\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen follow the usual \u003ccode\u003ecompare\u003c/code\u003e, \u003ccode\u003ecommit\u003c/code\u003e, verify your DNS/DHCP still works, \u003ccode\u003esave\u003c/code\u003e dance to apply \u0026amp; persist the changes.\u003c/p\u003e\n\u003cp\u003eNow when you login to the router and list the current DHCP leases, you\u0026rsquo;ll see the hostnames available - and you can now lookup the machines in local DNS via their hostname too. 🎉\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ show dhcp leases \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eIP address      Hardware Address   Lease expiration     Pool       Client Name\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e----------      ----------------   ----------------     ----       -----------\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e10.0.0.34       a8:1d:16:74:xx:yy  2021/03/14 16:38:27  trusted    cb2\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e10.0.0.40       b8:27:eb:c5:xx:yy  2021/03/14 16:36:13  trusted    picontrol1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e10.0.0.46       a8:1d:16:75:xx:yy  2021/03/14 16:37:48  trusted    cb3\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e10.0.0.93       14:f6:d8:53:xx:yy  2021/03/14 16:40:29  trusted    cb1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eOn my network currently these are Chromebooks, and Sonos speakers. I\u0026rsquo;ve also observed native SmartOS Zones behaving like this previously (I think they might have fixed this now.) I believe the device fails to send the current hostname (option 12) in either the DHCPDISCOVER or DHCPREQUEST packets.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eAlso, if I assign static host mappings to a device they vanish entirely from \u003ccode\u003eshow dhcp leases\u003c/code\u003e, which stops me being lazy and checking one place to figure out where a device is.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:3\"\u003e\n\u003cp\u003eTo find out if you\u0026rsquo;re using dnsmasq for DHCP, check \u003ccode\u003eshow service dhcp-server use-dnsmasq\u003c/code\u003e returns \u0026ldquo;enable\u0026rdquo;\u0026#160;\u003ca href=\"#fnref:3\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"Cleaning BODUM Bistro Coffee Grinder","date_published":"2021-01-17T11:41:00Z","date_modified":"2021-01-17T11:41:00Z","id":"https://caiustheory.com/cleaning-bodum-bistro-coffee-grinder/","url":"https://caiustheory.com/cleaning-bodum-bistro-coffee-grinder/","content_html":"\u003cp\u003eI\u0026rsquo;ve owned a \u003ca href=\"https://www.bodum.com/gb/en/10903-01uk-3-bistro\"\u003eBODUM Bistro Coffee Grinder\u003c/a\u003e for a number of years, and aside from occasionally running rice through to clean the grinding surfaces haven\u0026rsquo;t had any issues with it. Recently bought some new beans which are much oilier than ones I usually get, and after running most of them through ended up with the grinder failing to work.\u003c/p\u003e\n\u003cp\u003eThe failure was the mechanism sounding like it was okay for roughly a second, then the motor straining under load before what sounded like plastic gears jumping teeth. At this point I turned it off. Running it with an empty hopper worked fine, adding anything (either beans or ground coffee) to the hopper caused the load issue. On the third attempt it also had stopped self-feeding from the hopper, and trying to gently push beans/grounds through caused the stoppage above.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.youtube.com/user/HagmanDavid\"\u003eDavid Hagman\u003c/a\u003e has previously torn down his grinder and posted a \u003ca href=\"https://www.youtube.com/watch?v=w_Kl8Aw8iDU\"\u003evideo on YouTube showing the internals\u003c/a\u003e. I couldn\u0026rsquo;t see any obvious part of the internals that would be related to the failure I was experiencing, so I decided to start with cleaning it out and then continue with a strip down if that didn\u0026rsquo;t reveal anything.\u003c/p\u003e\n\u003cp\u003eTo start with the hopper came off, then the top half of the burr grinder lifts out vertically leaving a worm screw standing proud. I first started gently tapping the grinder unit upside down to free any stuck coffee, then escalated to a small bottle brush. There was still enough grounds stuck around the base of the screw mechanism I couldn\u0026rsquo;t reach with a narrow brush, so I switched to a bamboo barbequeue stick to loosen grounds and then tip them out.\u003c/p\u003e\n\u003cp\u003eAfter clearing out most of the base of the worm gear, whilst I had the unit upside down tapping out the loosened grounds I looked up the chute the grounds fall down into the jar normally to find it was blocked \u003cstrong\u003esolid\u003c/strong\u003e with ground coffee. Some gentle \u003ca href=\"https://www.youtube.com/watch?v=GSLTfRShSHY\"\u003erodding with the skewer\u003c/a\u003e to break it up and eventually I could see from the grinder mechanism through to the end of the chute.\u003c/p\u003e\n\u003cp\u003eOnce that was clear and everything removable had been thoroughly cleaned and dried, I reassembled and ran rice through it a few times starting with a really coarse grind and fed the result back through the hopper, getting finer each grind. Grinder now works flawlessly, and I guess lesson learned about checking the chute to make sure it\u0026rsquo;s clear more frequently.\u003c/p\u003e\n"},{"title":"Disable Google Autoupdater on macOS","date_published":"2020-12-14T16:14:53Z","date_modified":"2020-12-14T16:14:53Z","id":"https://caiustheory.com/disable-google-autoupdater-on-macos/","url":"https://caiustheory.com/disable-google-autoupdater-on-macos/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eFrom reading \u003ca href=\"https://chromeisbad.com\"\u003eChrome is Bad\u003c/a\u003e, it seems in some situations the updater (also known as keystone) can chew up CPU cycles. Whilst I\u0026rsquo;m not 100% convinced keystone continuously chews CPU, its launchctl configuration suggests it runs at least once an hour. Given I don\u0026rsquo;t use Chrome as my main browser, this is undesirable behaviour for me.\u003c/p\u003e\n\u003cp\u003eWith that in mind, I\u0026rsquo;ve decided to disable the background services rather than delete Chrome entirely. (I need it occasionally.) Stopping/unloading the services and fettling the config files to do nothing achieves this aim (and stops Chrome re-enabling them next launch), whilst leaving Chrome fully functional when needed.\u003c/p\u003e\n\u003col start=\"0\"\u003e\n\u003cli\u003e\n\u003cp\u003eUnload the currently loaded services\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003elaunchctl unload -w ~/Library/LaunchAgents/com.google.keystone.xpcservice.plist\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003elaunchctl unload -w ~/Library/LaunchAgents/com.google.keystone.agent.plist\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eEmpty the config files, so if launchd ever tries to launch them they\u0026rsquo;ll just error out\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u0026gt; ~/Library/LaunchAgents/com.google.keystone.xpcservice.plist\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u0026gt; ~/Library/LaunchAgents/com.google.keystone.agent.plist\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eChange ownership and permissions of these files so only root can write to the files\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003echmod \u003cspan class=\"m\"\u003e644\u003c/span\u003e ~/Library/LaunchAgents/com.google.keystone.xpcservice.plist\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003echmod \u003cspan class=\"m\"\u003e644\u003c/span\u003e ~/Library/LaunchAgents/com.google.keystone.agent.plist\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo chown root ~/Library/LaunchAgents/com.google.keystone.xpcservice.plist\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo chown root ~/Library/LaunchAgents/com.google.keystone.agent.plist\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eNow when I want to update Chrome once in a blue moon when I need it, I can navigate to \u003ca href=\"chrome://settings/help\"\u003echrome://settings/help\u003c/a\u003e to update (or from the UI, Chrome -\u0026gt; About Chrome.)\u003c/p\u003e\n"},{"title":"Let's Peek: A tale of finding \"Waypoint\"","date_published":"2020-10-15T20:00:00+01:00","date_modified":"2020-10-15T20:00:00+01:00","id":"https://caiustheory.com/lets-peek/","url":"https://caiustheory.com/lets-peek/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eFollowing a product launch at work earlier this year, I theorised if someone was watching the published lists of SSL Certificates they could potentially sneak a peak at things before they were publicised. Probably far too much noise to monitor continuously, but as a potential hint towards naming of things with a more targeted search it might be useful. Sites like \u003ca href=\"https://crt.sh/\"\u003ehttps://crt.sh/\u003c/a\u003e and \u003ca href=\"https://censys.io/certificates\"\u003ehttps://censys.io/certificates\u003c/a\u003e make these logs searchable and queryable.\u003c/p\u003e\n\u003cp\u003eFast forward to this week, where at \u003ca href=\"https://digital.hashiconf.com/\"\u003eHashiConf Digital\u003c/a\u003e \u003ca href=\"https://hashicorp.com/\"\u003eHashiCorp\u003c/a\u003e are announcing two new products, which they\u0026rsquo;ve been teasing for a month or so. Watching \u003ca href=\"https://boundaryproject.io/\"\u003eBoundary\u003c/a\u003e get announced in the HashiConf opening keynote I then wondered what the second project might be called.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve spent a chunk of the last month looking at various HashiCorp documentation for their projects, and I noticed they have a pattern recently of using \u003ccode\u003e\u0026lt;name\u0026gt;project.io\u003c/code\u003e as the product websites. The newly announced Boundary also fits this pattern.\u003c/p\u003e\n\u003cp\u003e🤔 Could I figure out the second product name 24 hours before public release? \u003ca href=\"https://twitter.com/Caius/status/1316435256875143168\"\u003eAmazingly, yes\u003c/a\u003e! 🎉\u003c/p\u003e\n\u003cp\u003eSearching at random for all certificates issued for \u003ccode\u003e*project.io\u003c/code\u003e was probably going to be a bit futile, so to narrow the search space slightly I started by looking at when \u003ccode\u003eboundaryproject.io\u003c/code\u003e had its certificate issued, and who by. The list of things I spotted were:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCommon name is \u0026ldquo;boundaryproject.io\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eIssued by LetsEncrypt (no real surprise there)\u003c/li\u003e\n\u003cli\u003eIssued on 2020-09-23\u003c/li\u003e\n\u003cli\u003eLeaf certificate\u003c/li\u003e\n\u003cli\u003eNot yet expired (still trusted)\u003c/li\u003e\n\u003cli\u003eNo alternate names in the certificate\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLoading up \u003ca href=\"https://censys.io/certificates\"\u003ehttps://censys.io/certificates\u003c/a\u003e and building a query for this, resulted in a regexp lookup against the common name, and an issued at date range of 10 days, just before and a week after the boundary certificate issued date.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eparsed.subject.common_name:/[a-z]+project\\.io/ AND \nparsed.issuer.organization.raw:\u0026quot;Let's Encrypt\u0026quot; AND \nparsed.validity.start:[\u0026quot;2020-09-20\u0026quot; TO \u0026quot;2020-09-30\u0026quot;] AND\ntags.raw:\u0026quot;leaf\u0026quot; AND \ntags.raw:\u0026quot;trusted\u0026quot;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e(\u003ca href=\"https://censys.io/certificates?q=parsed.subject.common_name%3A%2F%5Ba-z%5D%2Bproject%5C.io%2F+AND+parsed.issuer.organization.raw%3A%22Let%27s+Encrypt%22+AND+tags.raw%3A%22leaf%22+AND+tags.raw%3A%22trusted%22+AND+parsed.validity.start%3A%5B%222020-09-20%22+TO+%222020-09-30%22%5D\"\u003eRun the search yourself\u003c/a\u003e)\u003c/p\u003e\n\u003cp\u003eSearching brought back a couple of pages of results, I scanned them by eye and copied out the ones that only had the single name in the certificate which resulted in the following shortlist:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eboundaryproject.io\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eessenceproject.io\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elumiereproject.io\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etechproject.io\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eudproject.io\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003evesselproject.io\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ewaypointproject.io\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWe already know about Boundary, so the fact I found it in our list suggests the query \u003cem\u003emight\u003c/em\u003e have captured the new product site too. Loading all these sites in a web browser showed some had password protection on them (ooh!) and some just plain didn’t load (ooh!), and some others were blatently other things (boo!). Removing the latter ones left us with a much shorter list:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eessenceproject.io\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eudproject.io\u003c/code\u003e\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ewaypointproject.io\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAll domains on the internet have to point somewhere, using DNS records. On a hunch I looked up a couple of the existing HashiCorp websites to see if they happened to all point at the same IP Address(es).\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ host boundaryproject.io\nboundaryproject.io has address 76.76.21.21\n$ host nomadproject.io \nnomadproject.io has address 76.76.21.21\n$ host hashicorp.com | head -1\nhashicorp.com has address 76.76.21.21\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAh ha, now I wonder if any of the shortlist also points to \u003ccode\u003e76.76.21.21\u003c/code\u003e 🤔\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ host essenceproject.io | head -1\nessenceproject.io has address 198.185.159.145\n$ host udproject.io | head -1\nudproject.io has address 137.74.116.3\n$ host waypointproject.io\nwaypointproject.io has address 76.76.21.21\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e🎉 Excellent, \u003ca href=\"https://waypointproject.io\"\u003ehttps://waypointproject.io\u003c/a\u003e was a password protected site pointed at HashiCorp\u0026rsquo;s IP address 🎉\u003c/p\u003e\n\u003cp\u003eI then wondered if I could verify this somehow ahead of waiting for the second keynote. I firstly \u003ca href=\"https://twitter.com/Caius/status/1316435256875143168\"\u003etweeted about it\u003c/a\u003e but didn\u0026rsquo;t name Waypoint explicitly, just hid \u0026ldquo;way\u0026rdquo; and \u0026ldquo;point\u0026rdquo; in the tweet. I got a reply from \u003ca href=\"https://twitter.com/ksatirli/status/1316435508046815234\"\u003e@ksatirli\u003c/a\u003e which suggested it was correct (and then later \u003ca href=\"https://twitter.com/mitchellh/status/1316468703584645121\"\u003e@mitchellh\u003c/a\u003e confirmed it.\u003csup id=\"fnref:3\"\u003e\u003ca href=\"#fn:3\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e3\u003c/a\u003e\u003c/sup\u003e)\u003c/p\u003e\n\u003cp\u003eHashiCorp also does a lot in public, and all the source code and related materials are on GitHub so perhaps some of their commit messages or marketing sites will contain reference to Waypoint. One github search later across their organisation: \u003ca href=\"https://github.com/search?q=org%3Ahashicorp+waypoint\u0026amp;type=issues\"\u003ehttps://github.com/search?q=org%3Ahashicorp+waypoint\u0026amp;type=issues\u003c/a\u003e and I\u0026rsquo;d discovered a commit in the newly-public \u003ccode\u003ehashicorp/boundary-ui\u003c/code\u003e repo which references Waypoint: \u003ca href=\"https://github.com/hashicorp/boundary-ui/commit/346f76404d9a0c90dedbccdd3d2c228572eb5ec1\"\u003e346f76404\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003echore: tweak colors to match waypoint and for a11y\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eGood enough for me, now to wait and see what the project is for. Given it\u0026rsquo;s now all announced and live, you can just visit \u003ca href=\"https://waypointproject.io\"\u003ehttps://waypointproject.io\u003c/a\u003e to find out! (It\u0026rsquo;s so much cooler/useful than I\u0026rsquo;d hoped for.)\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eI so hope whoever registered this was going for UDP in the name, rather than UD Project.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eI\u0026rsquo;m a massive fan of IP address related quirks. Facebook\u0026rsquo;s IPv6 address contains \u003ccode\u003eface:b00c\u003c/code\u003e for example. A nice repeating \u003ccode\u003e76.76.21.21\u003c/code\u003e is almost IPv4 art somehow.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:3\"\u003e\n\u003cp\u003eSecrets are more fun when they are kept secret. 🥳\u0026#160;\u003ca href=\"#fnref:3\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"Tailscale, RFC1918, and DNS Rebinding Protection","date_published":"2020-07-16T21:00:00+01:00","date_modified":"2020-07-16T21:00:00+01:00","id":"https://caiustheory.com/tailscale-rfc1918-and-dns-rebinding-protection/","url":"https://caiustheory.com/tailscale-rfc1918-and-dns-rebinding-protection/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cstrong\u003eEdit:\u003c/strong\u003e \u003cem\u003eOriginally this post was written to be a workaround for Tailscale routing all DNS traffic over its own link when you configured it to push out existing DNS Server IPs. This turned out to be a bad assumption on my part. Thanks to \u003ca href=\"https://twitter.com/apenwarr\"\u003eapenwarr\u003c/a\u003e for helping me understand that shouldn\u0026rsquo;t be the case, and encouraging me to debug it properly rather than making assumptions.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eNaturally it turned out to be a \u003ca href=\"https://www.urbandictionary.com/define.php?term=pebkac\"\u003ePEBKAC\u003c/a\u003e. I\u0026rsquo;d pushed out \u003ccode\u003e162.159.25.4\u003c/code\u003e as the DNS Server IP which is a nameserver rather than a forwarder. This in turn meant people were getting empty answers back to DNS queries, which stopped once they quit tailscale. (Go figure, Tailscale removes the resolver from the network stack when it quits.) The post has been updated to remove that invalid assumption. 🤦🏻‍♂️\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eImagine we have a fleet of machines sat in a private network somewhere on a \u003ccode\u003e172.16.20.20/24\u003c/code\u003e IP range, with entries pointing at them published on public DNS servers. Eg, \u003ccode\u003edig +short workhorse.fake.tld\u003c/code\u003e returns \u003ccode\u003e172.16.20.21\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eInitially this all works swimmingly, until someone comes along that is using a DNS forwarder that with DNS rebinding protection enabled. Daniel Miessler has a \u003ca href=\"https://danielmiessler.com/blog/dns-rebinding-explained/\"\u003ewonderfully succinct explanation on his blog\u003c/a\u003e about DNS Rebinding attacks, but to protect against it you stop your resolver returning answers to DNS queries from public servers which resolve to IP addresses within standard internal network ranges. (ie, \u003ca href=\"https://www.ietf.org/rfc/rfc1918.txt\"\u003erfc1918\u003c/a\u003e.)\u003c/p\u003e\n\u003cp\u003eThis means for those users they can successfully connect to our Tailscale network and access everything by IPs directly, but can\u0026rsquo;t access any of the internal infrastructure by hostname. eg, \u003ccode\u003edig +short workhorse.fake.tld\u003c/code\u003e will return an empty answer for them.\u003c/p\u003e\n\u003cp\u003eOnce we figured out the root cause of that, for workarounds we figured we could either run a DNS forwarder within our own infrastructure, or get all our staff to change their home DNS settings and hope they were never on locked down networks ever again.\u003c/p\u003e\n\u003cp\u003eWe chose the former, and thankfully \u003ca href=\"http://www.thekelleys.org.uk/dnsmasq/doc.html\"\u003ednsmasq\u003c/a\u003e is really easy to configure in this fashion and we already have a node which is acting as the tailscale subnet relay, so we dropped the following config in \u003ccode\u003e/etc/dnsmasq.conf\u003c/code\u003e on there:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# Only listen for requests from VPN/local for debugging\ninterface=tailscale0\ninterface=lo\n\n# Google DNS\nserver=8.8.8.8\nserver=8.8.4.4\n# Quad9\nserver=9.9.9.9\n# Cloudflare\nserver=1.1.1.1\nserver=1.0.0.1\n# Race all servers to see which wins\nall-servers\n\n# Try and stop DNS rebinding, except where we expect it to happen\nbogus-priv\nstop-dns-rebind\nrebind-localhost-ok\nrebind-domain-ok=/fake.tld/\n\ndomain-needed\nfilterwin2k\nno-poll\nno-resolv\ncache-size=10000\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOne quick puppet run later, and our Tailscale subnet relays are happily running both tailscale and dnsmasq, serving out answers as fast as they can to other Tailscale nodes. Add port 53 to the \u003ca href=\"https://www.tailscale.com/kb/1018/acls\"\u003eTailscale ACL\u003c/a\u003e and away we went.\u003c/p\u003e\n"},{"title":"RSpec Given/When/Then with symbols","date_published":"2020-04-22T18:30:00+01:00","date_modified":"2020-04-22T18:30:00+01:00","id":"https://caiustheory.com/rspec-given/when/then-with-symbols/","url":"https://caiustheory.com/rspec-given/when/then-with-symbols/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eHaving a need to write some BDD-esque tests without the need of putting them in front of non-technical people, I was recently playing around with \u003ca href=\"https://relishapp.com/rspec/rspec-rails/docs/feature-specs/feature-spec\"\u003erspec feature specs\u003c/a\u003e. Where I\u0026rsquo;ve used these previously we\u0026rsquo;ve eventually run into curation issues where the specs are outdated, brittle and require so much maintenance we\u0026rsquo;ve generally ended up lobbing cucumber into the project as a stopgap.\u003c/p\u003e\n\u003cp\u003eThis is due to ending up with feature specs like the following, which lead you to having to parse the code mentally to work out what it\u0026rsquo;s testing:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eRSpec\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efeature\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Admin: Posts\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003escenario\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Authoring a post\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecreate\u003c/span\u003e \u003cspan class=\"ss\"\u003e:user\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:admin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elogin_as\u003c/span\u003e \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evisit\u003c/span\u003e \u003cspan class=\"n\"\u003enew_admin_post_path\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efill_in\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Title\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;RSpec feature specs\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efill_in\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Body\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Some piffle about feature specs\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eclick_on\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Publish!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evisit\u003c/span\u003e \u003cspan class=\"n\"\u003eroot_url\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eexpect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto\u003c/span\u003e \u003cspan class=\"n\"\u003ehave_content\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;RSpec feature specs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter some reading around, I eventually stumbled back across \u003ca href=\"https://www.futurelearn.com/info/blog/how-we-write-readable-feature-tests-with-rspec\"\u003ethis idea from Future Learn\u003c/a\u003e where they lay out the above test by splitting it into private methods within the feature block, but leaving it more readable to future readers. I then found \u003ca href=\"https://www.madetech.com/blog/feature-testing-with-rspec\"\u003eMade Tech\u0026rsquo;s take on this same idea\u003c/a\u003e, and riffing off the both of them ended up with the following instead:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eRSpec\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efeature\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Admin: Posts\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003escenario\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Authoring a post\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003egiven_i_am_logged_in_as_an_admin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ewhen_i_publish_a_new_post\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ethen_i_see_the_post_on_the_homepage\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kp\"\u003eprotected\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003egiven_i_am_logged_in_as_an_admin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecreate\u003c/span\u003e \u003cspan class=\"ss\"\u003e:user\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:admin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elogin_as\u003c/span\u003e \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ewhen_i_publish_a_new_post\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evisit\u003c/span\u003e \u003cspan class=\"n\"\u003enew_admin_post_path\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efill_in\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Title\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;RSpec feature specs\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efill_in\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Body\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Some piffle about feature specs\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eclick_on\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Publish!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ethen_i_see_the_post_on_the_homepage\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evisit\u003c/span\u003e \u003cspan class=\"n\"\u003eroot_url\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eexpect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto\u003c/span\u003e \u003cspan class=\"n\"\u003ehave_content\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;RSpec feature specs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow this is fine, but writing lots_of_names_with_underscores_in_is_a_trifle \u003cstrong\u003eirritating\u003c/strong\u003e. Now I remember \u003ca href=\"https://weirichinstitute.com/about\"\u003eJim Weirich\u003c/a\u003e\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e showing off \u003ca href=\"https://github.com/rspec-given/rspec-given\"\u003erspec-given\u003c/a\u003e at a conference a few years ago, and wondered if that would solve my problem here of wanting to have runtime warn me when my methods are misspelled or missing, without having_to_underscore_them.\u003c/p\u003e\n\u003cp\u003eNow rspec-given would let me do that, but I\u0026rsquo;d have to switch from calling them all in turn inside a scenario block to calling them inside context blocks and passing blocks to each of the \u003ccode\u003eGiven\u003c/code\u003e, \u003ccode\u003eWhen\u003c/code\u003e, etc methods. I think it would be something like (warning, untested)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eRspec\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efeature\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Admin: Posts\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eGiven\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecreate\u003c/span\u003e \u003cspan class=\"ss\"\u003e:user\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:admin\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eGiven\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003elogin_as\u003c/span\u003e \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003econtext\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;authoring a post\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"no\"\u003eWhen\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003evisit\u003c/span\u003e \u003cspan class=\"n\"\u003enew_admin_post_path\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"no\"\u003eWhen\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003efill_in\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"err\"\u003e…\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"no\"\u003eThen\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003evisit\u003c/span\u003e \u003cspan class=\"n\"\u003eroot_url\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"no\"\u003eAnd\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eexpect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto\u003c/span\u003e \u003cspan class=\"n\"\u003ehave_content\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;RSpec feature specs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow this didn\u0026rsquo;t \u003cem\u003equite\u003c/em\u003e fit with what I wanted. However, I did wonder if it was possible to go down the route of having a \u003ccode\u003eGiven\u003c/code\u003e method that takes a token to identify the code it should call. (A method if you will.) It\u0026rsquo;s possible in ruby to call a method starting with a Capital letter, but convention dictates those are usually class/module names (constants) rather than methods.\u003c/p\u003e\n\u003cp\u003eA little bit of hacking later and this is what I ended up getting working:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eRSpec\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efeature\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Admin: Posts\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003escenario\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Authoring a post\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"no\"\u003eGiven\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026#34;I am logged in as an admin\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"no\"\u003eWhen\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026#34;I publish a new post\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"no\"\u003eThen\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026#34;I see the post on the homepage\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kp\"\u003eprotected\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003edef_Given\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026#34;I am logged in as an admin\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecreate\u003c/span\u003e \u003cspan class=\"ss\"\u003e:user\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:admin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elogin_as\u003c/span\u003e \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003edef_When\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026#34;I publish a new post\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evisit\u003c/span\u003e \u003cspan class=\"n\"\u003enew_admin_post_path\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efill_in\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Title\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;RSpec feature specs\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efill_in\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Body\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Some piffle about feature specs\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eclick_on\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Publish!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003edef_Then\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026#34;I see the post on the homepage\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003evisit\u003c/span\u003e \u003cspan class=\"n\"\u003eroot_url\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eexpect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto\u003c/span\u003e \u003cspan class=\"n\"\u003ehave_content\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;RSpec feature specs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow there\u0026rsquo;s two extra things that makes this easier for me to write than underscored methods. Ruby doesn\u0026rsquo;t only allow \u003ccode\u003e:foo\u003c/code\u003e as a symbol, it also allows \u003ccode\u003e:\u0026quot;foo bar\u0026quot;\u003c/code\u003e for writing a symbol. You can then define a method based on that even though it has spaces in the method name.\u003c/p\u003e\n\u003cp\u003eMy text editor\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e also autocompletes ruby symbols from partial matches, which makes it easy to write out what I want in the scenario, run the spec and find out what methods need defining, then define the methods using autocomplete to save copy/pasting everything.\u003c/p\u003e\n\u003cp\u003eBy using actual methods for these, we get a couple of other happy accidents along the way. Most ruby installs now include \u003ca href=\"https://github.com/ruby/did_you_mean\"\u003edid_you_mean\u003c/a\u003e out the box, which suggests methods like the one you called if your method results in a \u003ccode\u003eNoMethodError\u003c/code\u003e. This works quite nicely, you end up with something like\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eundefined method `When I pblish a new post\u0026#39; for #\u0026lt;RSpec::ExampleGroups::AdminPosts:0x00007faf1f9fc4c0\u0026gt;\n\n    Did you mean? When I publish a new post\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd then if you just run it without implementing any of the helper methods at all, you get a nice \u003ccode\u003eNoMethodError\u003c/code\u003e telling you exactly what you need to implement:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eNoMethodError:\n  undefined method `Given I am logged in as an admin\u0026#39; for #\u0026lt;RSpec::ExampleGroups::AdminPosts:0x00007fbd06598498\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe magic behind that makes all this work is in \u003ca href=\"https://gist.github.com/caius/606b80252b176e353fe0893f8888dbbf\"\u003e\u003ccode\u003espec/support/given_when_then.rb\u003c/code\u003e\u003c/a\u003e, which is not terrible, but also probably not a great idea. 🙃\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003e😿\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003e\u003ca href=\"https://macromates.com\"\u003eTextMate 2\u003c/a\u003e\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"curl --resolve","date_published":"2019-09-14T18:10:00+01:00","date_modified":"2019-09-14T18:10:00+01:00","id":"https://caiustheory.com/curl--resolve/","url":"https://caiustheory.com/curl--resolve/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSometimes it\u0026rsquo;s useful to be able to craft a request to one server, using a DNS name that\u0026rsquo;s either not defined or currently pointed to a different IP. (Migrating webservers, testing a new webserver config out, etc.)\u003c/p\u003e\n\u003cp\u003eHistorically for HTTP calls this was easy, just set the \u003ccode\u003eHost\u003c/code\u003e header as you make the http request to the IP directly:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\tcurl -H \u0026quot;Host: caiustheory.com\u0026quot; http://10.200.0.1/\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eHowever, HTTPS throws a bit of a spanner in the works, if we just try to connect using an overridden \u003ccode\u003eHost\u003c/code\u003e header, we get an error back from the server if it\u0026rsquo;s not configured with a certificate for the IP address:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\t$ curl -H \u0026quot;Host: caiustheory.com\u0026quot; https://10.200.0.1/\n\tcurl: (51) SSL: no alternative certificate subject name matches target host name '10.200.0.1'\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eUsually at this point I\u0026rsquo;d just start editing \u003ccode\u003e/etc/hosts\u003c/code\u003e to add \u003ccode\u003e10.200.0.1 caius.name\u003c/code\u003e to it and carry on testing. This is a pain when you\u0026rsquo;re testing more than one server, or you\u0026rsquo;re on a machine where you don\u0026rsquo;t have root access to edit \u003ccode\u003e/etc/hosts\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIn later versions of curl there\u0026rsquo;s a solution for this built into the binary, in the form of the \u003ccode\u003e--resolve\u003c/code\u003e flag. You can tell it to override the DNS lookup for a specific hostname/port combination. This in turn means that the correct host is forwarded to the server for the correct SSL certificate to be chosen to serve the request based on host.\u003c/p\u003e\n\u003cp\u003eIt takes the form \u003ccode\u003e--resolve HOST:PORT:IP\u003c/code\u003e where \u003ccode\u003eHOST\u003c/code\u003e is the human-friendly host, \u003ccode\u003ePORT\u003c/code\u003e is the webserver\u0026rsquo;s port (convention is 80 for HTTP, 443 for HTTPS) and IP being the destination IP you want to hit. (As opposed to the one in DNS currently.)\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ curl --silent --head --resolve caiustheory.com:443:10.200.0.1 https://caiustheory.com | head -n1\nHTTP/2 200 \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd voila, you don\u0026rsquo;t need to fiddle with editing \u003ccode\u003e/etc/hosts\u003c/code\u003e. Just use \u003ccode\u003e--resolve\u003c/code\u003e to hit a different IP for a given host.\u003c/p\u003e\n"},{"title":"Cheaper Oil for Mini One R50","date_published":"2019-04-02T12:00:00Z","date_modified":"2019-04-02T12:00:00Z","id":"https://caiustheory.com/cheaper-oil-for-mini-one-r50/","url":"https://caiustheory.com/cheaper-oil-for-mini-one-r50/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eMy Mini One 2003 R50 1.6 litre petrol engine takes specific BMW 5w30 Longlife-04 oil. (I believe the R52 and R53 models take the same oil too.) The oil is 5w30 fully synthetic made to BMW\u0026rsquo;s exacting standards.\u003c/p\u003e\n\u003cp\u003eThe cheapest I\u0026rsquo;ve found to buy currently is a GM (Vauxhall/Opel) manufactured one, made to BMW\u0026rsquo;s specifications. Searching for something like \u0026ldquo;dexos 2 5w30 gm\u0026rdquo; \u003ca href=\"https://www.ebay.co.uk/sch/i.html?_nkw=dexos+2+5w30+gm\"\u003eon ebay\u003c/a\u003e finds them at about £20 for 5 litres with free delivery in UK. (Comparatively, an equivalent from Castrol is about £50 at time of writing.)\u003c/p\u003e\n\u003cp\u003eSounds like a small saving, but if your Mini is anything like mine it needs topping up once a month or so, and I do a full oil/filter change every 5k miles as an attempt at longevity. Soon adds up.\u003c/p\u003e\n"},{"title":"Download All Your Gists","date_published":"2019-03-17T14:32:30Z","date_modified":"2019-03-17T14:32:30Z","id":"https://caiustheory.com/download-all-your-gists/","url":"https://caiustheory.com/download-all-your-gists/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eOver time I\u0026rsquo;ve managed to build up quite the collection of \u003ca href=\"https://gist.github.com/\"\u003eGists\u003c/a\u003e over at Github, including secret ones there\u0026rsquo;s about 1200 currently. Some of these have useful code in, some are just garbage output. I\u0026rsquo;d quite like a local copy either way, so I can easily search\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e across them.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eInstall the \u003ccode\u003egist\u003c/code\u003e command from Github\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebrew install gist\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eLogin to your Github Account through the gist tool (it\u0026rsquo;ll prompt for your login credentials, then generate you an API Token to allow it future access.)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egist --login\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eCreate a folder, go inside it and download all your gists!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir gist_archive\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e gist_archive\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e repo in \u003cspan class=\"k\"\u003e$(\u003c/span\u003egist -l \u003cspan class=\"p\"\u003e|\u003c/span\u003e awk \u003cspan class=\"s1\"\u003e\u0026#39;{ print $1 }\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e git clone \u003cspan class=\"nv\"\u003e$repo\u003c/span\u003e 2\u0026gt; /dev/null\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eNow you have a snapshot of all your gists. To update them in future, you can run the above for any new gists, and update all the existing ones with:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e gist_archive\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e i in */\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e git pull --rebase\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eNow go forth and search out your favourite snippet you saved years ago and forgot about!\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003e\u003ca href=\"https://beyondgrep.com\"\u003eack\u003c/a\u003e, \u003ca href=\"https://geoff.greer.fm/ag/\"\u003eag\u003c/a\u003e, \u003ca href=\"https://www.freebsd.org/cgi/man.cgi?query=grep\u0026amp;sektion=\u0026amp;n=1\"\u003egrep\u003c/a\u003e, \u003ca href=\"https://github.com/BurntSushi/ripgrep\"\u003eripgrep\u003c/a\u003e, etc. Pick your flavour.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"Prefixing Git Branch With Initials","date_published":"2018-11-21T20:30:40Z","date_modified":"2018-11-21T20:30:40Z","id":"https://caiustheory.com/prefixing-git-branch-with-initials/","url":"https://caiustheory.com/prefixing-git-branch-with-initials/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eWorking somewhere where we prefix our branches with the creator\u0026rsquo;s initials, I sometimes forget to do so.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e This leads to me having to rename the branch, typing out the whole name again after adding \u003ccode\u003ecd/\u003c/code\u003e to the start of it.\u003c/p\u003e\n\u003cp\u003eComputers are meant to solve repetitive problems for us, so let\u0026rsquo;s put it to work in this case too. My \u003ca href=\"https://github.com/caius/BinFiles\"\u003e~/bin\u003c/a\u003e contains \u003ca href=\"https://github.com/caius/BinFiles/blob/c7c5c6ababd70e65fa4d072bf8392aaa014c607c/git-current-branch\"\u003e\u003ccode\u003egit current-branch\u003c/code\u003e\u003c/a\u003e, which returns the current branch name.\u003c/p\u003e\n\u003cp\u003eIf we hardcode the initials, this becomes a simple command to recall from our history:\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit branch --move --force cd/\u003cspan class=\"k\"\u003e$(\u003c/span\u003egit current-branch\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBut computers are supposed to solve all repetitive work, including knowing who I am, right? Correct, my local user account knows my full name, so we can work out my initials from that. Lets lean on the \u003ccode\u003eid(1)\u003c/code\u003e command to lookup the user\u0026rsquo;s details then strip it down to just the initials.\u003csup id=\"fnref:3\"\u003e\u003ca href=\"#fn:3\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e3\u003c/a\u003e\u003c/sup\u003e\u003csup id=\"fnref:4\"\u003e\u003ca href=\"#fn:4\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e4\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eid -F\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;Caius Durling\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eid -F \u003cspan class=\"p\"\u003e|\u003c/span\u003e sed -Ee \u003cspan class=\"s1\"\u003e\u0026#39;s/(^| )(.)[^ ]+/\\2/g\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e tr \u003cspan class=\"s1\"\u003e\u0026#39;A-Z\u0026#39;\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;a-z\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; cd\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBingo, we can wrap that into a subshell passed to the branch move command and we\u0026rsquo;re done in a one-liner.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit branch --move --force \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003eid -F \u003cspan class=\"p\"\u003e|\u003c/span\u003e sed -Ee \u003cspan class=\"s1\"\u003e\u0026#39;s/(^| )(.)[^ ]+/\\2/g\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e tr \u003cspan class=\"s1\"\u003e\u0026#39;A-Z\u0026#39;\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;a-z\u0026#39;\u003c/span\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e/\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003egit current-branch\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eI don\u0026rsquo;t follow that policy for my personal repos, or working on forks of other people\u0026rsquo;s code. And I\u0026rsquo;m human, so I forget.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eYou can also replace \u003ccode\u003e--move --force\u003c/code\u003e with \u003ccode\u003e-M\u003c/code\u003e: \u003ccode\u003egit branch -M newname\u003c/code\u003e\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:3\"\u003e\n\u003cp\u003eOn macOS you can use \u003ccode\u003eid -F\u003c/code\u003e to return the full name of the user. Doing this on other platforms is left as an exercise for the reader.\u0026#160;\u003ca href=\"#fnref:3\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:4\"\u003e\n\u003cp\u003eYes, this is an incredibly naive way to initialize a name, but it\u0026rsquo;s good enough for the people I work with. Handling edge cases is left as … you got it, an exercise for the reader.\u0026#160;\u003ca href=\"#fnref:4\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"Struct-uring your data","date_published":"2018-11-09T00:31:00Z","date_modified":"2018-11-09T00:31:00Z","id":"https://caiustheory.com/struct-uring-your-data/","url":"https://caiustheory.com/struct-uring-your-data/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eIn Ruby it\u0026rsquo;s easy to structure data in hashes and pass it around, and usually that leads to errors with calling methods on \u003ccode\u003enil\u003c/code\u003e, or misspelling the name of a key, or some such silly bug that we might catch earlier given a defined object with a custom Class behind it. But it\u0026rsquo;s so much work to create a Class just to represent some grab bag of data we\u0026rsquo;ve been handed, right? Well, maybe!\u003c/p\u003e\n\u003cp\u003eLets say we have some event data that we\u0026rsquo;re being sent and we want to do some stuff with it in memory, we could just represent this as an array of hashes:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eevents\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Writing\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"ss\"\u003eduration\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e15\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"ss\"\u003estarted\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"ss\"\u003efinished\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Evening walk\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"ss\"\u003eduration\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e60\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"ss\"\u003estarted\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"ss\"\u003efinished\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is not a \u003cem\u003ebad\u003c/em\u003e way to represent the data, but if we want to start asking questions of it like \u0026ldquo;find all events currently happening\u0026rdquo; it becomes trickier. We could filter the collection to just those \u0026ldquo;in progress\u0026rdquo; events with the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eevents\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselect\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003eevent\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eevent\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:started\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"n\"\u003eevent\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:finished\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext time someone reads this though, they have to figure out what it means to have an event that\u0026rsquo;s started but not finished. Also what happens when someone in future misremembers \u003ccode\u003e:finished\u003c/code\u003e as \u003ccode\u003e:completed\u003c/code\u003e when running over the data in new code?\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e Wouldn\u0026rsquo;t it be better if we could do the following instead?\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eevents\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselect\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003eevent\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eevent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ein_progress?\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAn easy way to do this is to just create a Struct for the event, with the extra method defined internally. Whilst we\u0026rsquo;re in there, we could add a couple more methods to make us querying the state of boolean attributes nicer to read(eh?)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eEvent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eStruct\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003e:name\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:duration\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:started\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:finished\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ealias_method\u003c/span\u003e \u003cspan class=\"ss\"\u003e:started?\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:started\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ealias_method\u003c/span\u003e \u003cspan class=\"ss\"\u003e:finished?\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:finished\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ein_progress?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003estarted?\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"n\"\u003efinished?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd then to create the objects, we can either use the positional arguments to \u003ccode\u003e.new\u003c/code\u003e (same order as the symbols given to \u003ccode\u003eStruct.new\u003c/code\u003e), or tap the object and use the setters directly for each attribute.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eevents\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eEvent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Writing\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e15\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"no\"\u003eEvent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Evening Walk\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eduration\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e60\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estarted\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efinished\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; [#\u0026lt;struct Event\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#      name=\u0026#34;Writing\u0026#34;,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#      duration=15,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#      started=true,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#      finished=false\u0026gt;,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#     #\u0026lt;struct Event\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#      name=\u0026#34;Evening Walk\u0026#34;,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#      duration=60,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#      started=true,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#      finished=false\u0026gt;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can use our easier-to-read code for selecting all in-progress events, or ignoring all of those. Or if we just want to grab all finished events, we now have a method to call—\u003ccode\u003eEvent#finished?\u003c/code\u003e—that conveys the intent of what it returns without having to look up the data structure of the hash to work out if that field is a \u003ccode\u003eString\u003c/code\u003e or \u003ccode\u003eBoolean\u003c/code\u003e.\u003csup id=\"fnref:3\"\u003e\u003ca href=\"#fn:3\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e3\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eFor super-powered structs, you don\u0026rsquo;t even need to assign them to a \u003ccode\u003eConstant\u003c/code\u003e. You can just assign them to normal variables and use them locally in that scope without needing to define a constant.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eGrabber\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ecall\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eStruct\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003e:success\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:output\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003ealias_method\u003c/span\u003e \u003cspan class=\"ss\"\u003e:success?\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:success\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003egrab_data\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kp\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat\u0026rsquo;ll handily return you an object you can interrogate for \u003ccode\u003esuccess?\u003c/code\u003e and ask for the \u003ccode\u003eoutput\u003c/code\u003e if it was successful. And no Constants were created in the making of this method. 🎉\u003c/p\u003e\n\u003cp\u003eKeep an eye out for where you can Struct-ure your data. It might be more often than you expect.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eIt would always returns \u003ccode\u003etrue\u003c/code\u003e - \u003ccode\u003e!nil\u003c/code\u003e.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eThat\u0026rsquo;s \u003ccode\u003eevents.select(\u0026amp;:in_progress?)\u003c/code\u003e for the golfers amongst you.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:3\"\u003e\n\u003cp\u003e\u003ccode\u003e…?\u003c/code\u003e methods in ruby are truthy/falsy by convention.\u0026#160;\u003ca href=\"#fnref:3\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n"},{"title":"Running rails tests under CircleCI 2.0 with MariaDB","date_published":"2018-07-11T18:30:00Z","date_modified":"2018-07-11T18:30:00Z","id":"https://caiustheory.com/running-rails-tests-under-circleci-2.0-with-mariadb/","url":"https://caiustheory.com/running-rails-tests-under-circleci-2.0-with-mariadb/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003ca href=\"https://circleci.com\"\u003eCircleCI\u003c/a\u003e have released their version 2.0 platform, which is based on top of docker and moves the configuration for each project into a config file in the git repository.\u003c/p\u003e\n\u003cp\u003eThey have a bunch of documentation at \u003ca href=\"https://circleci.com/docs/2.0/\"\u003ehttps://circleci.com/docs/2.0/\u003c/a\u003e. Basic gist is the config file lives at \u003ccode\u003e.circleci/config.yml\u003c/code\u003e and defines which images to run a series of commands in. You can either specify jobs to run in series, or a workflow containing jobs which can depend on each other and/or run in parallel.\u003c/p\u003e\n\u003cp\u003eThe first step is finding a base image that contains ruby, node and chrome/chromedriver so the the app runs, assets compile and rails feature specs work respectively.\u003c/p\u003e\n\u003cp\u003eThe available images for ruby are listed at \u003ca href=\"https://github.com/CircleCI-Public/circleci-dockerfiles/tree/master/ruby/images\"\u003ehttps://github.com/CircleCI-Public/circleci-dockerfiles/tree/master/ruby/images\u003c/a\u003e, and the mariadb images are listed at \u003ca href=\"https://github.com/CircleCI-Public/circleci-dockerfiles/tree/master/mariadb/images\"\u003ehttps://github.com/CircleCI-Public/circleci-dockerfiles/tree/master/mariadb/images\u003c/a\u003e. For the ruby images you\u0026rsquo;ll want to use the \u003ccode\u003e…-node-browsers\u003c/code\u003e image as it has Node.js for assets and Chrome/chromedriver installed for headless browser testing.\u003c/p\u003e\n\u003cp\u003eSo the start of our config file looks something like the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eversion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"nt\"\u003eworking_directory\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;~/project\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"nt\"\u003edocker\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;circleci/ruby:2.4.1-node-browsers\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eRAILS_ENV\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;test\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;mariadb:10.2.12\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_DATABASE\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;app_test\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_USER\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;root\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_ALLOW_EMPTY_PASSWORD\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_HOST\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;localhost\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce we have that then we can start on setting up our rails environment to the point we can run tests. First of all we need to install all our ruby dependencies via \u003ca href=\"https://bundler.io\"\u003ebundler\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Install ruby dependencies\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;bundle install --path vendor/bundle\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we need to install our JS dependencies via \u003ca href=\"https://yarnpkg.com/lang/en/\"\u003eyarn\u003c/a\u003e, in much the same way as we did for the ruby dependencies.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Install js dependencies\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;yarn install\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we need to sort out our database. There\u0026rsquo;s a chance that the docker instance for MariaDB hasn\u0026rsquo;t come up yet, so we can lean on a tool called \u003ccode\u003edockerize\u003c/code\u003e to wait for it to be available. Then we can ask rails to go ahead and setup our test database.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Wait for database to be available\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;dockerize -wait tcp://127.0.0.1:3306 -timeout 1m\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Setup database\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;bundle exec rake db:setup\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd then finally we can run our tests as the final step.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Run tests\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;bundle exec rspec\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePutting it all together, we have the following in \u003ccode\u003e.circleci/config.yml\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eversion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"nt\"\u003eworking_directory\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;~/project\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"nt\"\u003edocker\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;circleci/ruby:2.4.1-node-browsers\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eRAILS_ENV\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;test\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;mariadb:10.2.12\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_DATABASE\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;app_test\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_USER\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;root\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_ALLOW_EMPTY_PASSWORD\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eMYSQL_HOST\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;localhost\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Install ruby dependencies\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;bundle install --path vendor/bundle\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Install js dependencies\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;yarn install\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Wait for database to be available\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;dockerize -wait tcp://127.0.0.1:3306 -timeout 1m\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Setup database\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;bundle exec rake db:setup\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Run tests\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;bundle exec rspec\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMay your test runs always be green and your bugs be squished.\u003c/p\u003e\n"},{"title":"Overcommit git hooks: puppet validate","date_published":"2018-07-05T14:44:41Z","date_modified":"2018-07-05T14:44:41Z","id":"https://caiustheory.com/overcommit-git-hooks-puppet-validate/","url":"https://caiustheory.com/overcommit-git-hooks-puppet-validate/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eComputers are great at automatically checking things, and git has a mechanism for running hooks before events happen. Bosh these two things together and you can have git trigger anything you like before you\u0026rsquo;re allowed to commit, which in turn means you can sanity check exactly what you\u0026rsquo;re committing to make sure it meets whatever criteria you have.\u003c/p\u003e\n\u003cp\u003eTo make it easy to manage my git hooks in a consistent fashion, I use a tool called \u003ca href=\"https://github.com/brigade/overcommit\"\u003eovercommit\u003c/a\u003e. This comes with a config file to tell it what you want triggered when, and a bunch of standard plugins to choose from.\u003c/p\u003e\n\u003cp\u003eIn a \u003ca href=\"https://puppet.com\"\u003epuppet\u003c/a\u003e repo for instance, I have it check\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://bundler.io\"\u003ebundler\u003c/a\u003e is happy everything\u0026rsquo;s installed\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://puppet-lint.com\"\u003epuppet-lint\u003c/a\u003e is happy with any changed puppet files\u003c/li\u003e\n\u003cli\u003eAny JSON or YAML files involved in the commit have valid syntax\u003c/li\u003e\n\u003cli\u003eAny shell scripts are valid (according to \u003ca href=\"https://www.shellcheck.net\"\u003eshellcheck\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003eThere is no trailing whitespace left in files\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis usually means something like the following in \u003ccode\u003e.overcommit.yml\u003c/code\u003e in the git repo\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ePreCommit\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eBundleCheck\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenabled\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eJsonSyntax\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenabled\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ePuppetLint\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenabled\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eShellCheck\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenabled\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eYamlSyntax\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenabled\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eon_warn\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003efail\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the most part this works absolutely great. I have on occasion noticed that \u003ca href=\"http://puppet-lint.com\"\u003epuppet-lint\u003c/a\u003e will let invalid puppet syntax slip through though which is irritating to find after you\u0026rsquo;ve pushed the changes up to the puppetmaster. The \u003ca href=\"https://puppet.com\"\u003epuppet\u003c/a\u003e command line tool has a validate subcommand however, so we can hook that into overcommit as a custom hook in the repo.\u003c/p\u003e\n\u003cp\u003eTo do this we need to add our custom hook into the right directory, and we\u0026rsquo;re adding a hook to run before commits, so it goes into \u003ccode\u003e.git-hooks/pre_commit\u003c/code\u003e. Lets name it for what it does, validating puppet syntax. So into \u003ccode\u003epuppet_validate.rb\u003c/code\u003e we put the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# .git-hooks/pre_commit/puppet_validate.rb\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003emodule\u003c/span\u003e \u003cspan class=\"nn\"\u003eOvercommit::Hook::PreCommit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003ePuppetValidate\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"no\"\u003eBase\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003erun\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003eerrors\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e[]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eexecute\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sx\"\u003e%w(puppet parser validate)\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:args\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eapplicable_files\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"ss\"\u003e:pass\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esuccess?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:fail\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estderr\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we need to tell \u003ca href=\"https://github.com/brigade/overcommit\"\u003eovercommit\u003c/a\u003e that it needs to run this custom hook as a check whenever we commit, that goes into \u003ccode\u003e.overcommit.yml\u003c/code\u003e under the \u003ccode\u003ePreCommit\u003c/code\u003e key:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ePreCommit\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ePuppetValidate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenabled\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003edescription\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;Validates puppet syntax\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003einclude\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;**/*.pp\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHey voila, along with all our other safety checks we\u0026rsquo;re now checking that any puppet files added or changed in the repo have valid syntax.\u003c/p\u003e\n"},{"title":"Cleaning up locally installed Rubies \u0026 Gems","date_published":"2017-11-12T11:30:24Z","date_modified":"2017-11-12T11:30:24Z","id":"https://caiustheory.com/cleaning-up-locally-installed-rubies-gems/","url":"https://caiustheory.com/cleaning-up-locally-installed-rubies-gems/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eGiven the following is broadly true:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eRubies installed to \u003ccode\u003e~/.rubies\u003c/code\u003e (via ruby-install most likely)\u003c/li\u003e\n\u003cli\u003eGems for each ruby version installed under ~/.gem (via chruby most likely)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhat to do when you want to reclaim some disk space? Delete unused ruby versions of course! Pretty straight forward, look in ~/.rubies for ones you want to remove, then delete them.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ ls ~/.rubies\nruby-2.0.0\nruby-2.1.7\nruby-2.3.1\nruby-2.4.1\n\n$ rm -r ~/.rubies/ruby-{2.0.0,2.1.7}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen the problem is we\u0026rsquo;re left with artifacts hanging around, namely any gems we installed for ruby 2.0.0 or 2.1.7 are still present under \u003ccode\u003e~/.gem\u003c/code\u003e using up disk space. We could go through and find them by hand, or we could get the computer to delete anything under \u003ccode\u003e~/.gem\u003c/code\u003e that doesn\u0026rsquo;t have a corresponding runtime under \u003ccode\u003e~/.rubies\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ediff --old-line-format\u003cspan class=\"o\"\u003e=\u003c/span\u003e --unchanged-line-format\u003cspan class=\"o\"\u003e=\u003c/span\u003e --new-line-format\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"nv\"\u003e$HOME\u003c/span\u003e/.gem/ruby/%L \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  \u0026lt;\u003cspan class=\"o\"\u003e(\u003c/span\u003els ~/.rubies \u003cspan class=\"p\"\u003e|\u003c/span\u003e sed -Ee \u003cspan class=\"s1\"\u003e\u0026#39;s/ruby-|-p[0-9]+//g\u0026#39;\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u0026lt;\u003cspan class=\"o\"\u003e(\u003c/span\u003els ~/.gem/ruby\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  \u003cspan class=\"p\"\u003e|\u003c/span\u003e xargs -pL1 rm -r\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e(\u003ccode\u003exargs -pL1\u003c/code\u003e will prompt with each command it wants to run before running it - answer \u003ccode\u003ey\u003c/code\u003e to proceed, anything else to prevent it running that command. Lets you see what ruby versions it is removing before it does so.)\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ ls ~/.gem/ruby\n2.0.0\n2.1.7\n2.3.1\n2.4.1\n\n$ diff --old-line-format= --unchanged-line-format= --new-line-format=$HOME/.gem/ruby/%L \\\n  \u0026lt;(ls ~/.rubies | sed -Ee 's/ruby-|-p[0-9]+//g') \u0026lt;(ls ~/.gem/ruby) \\\n  | xargs -pL1 rm -r\nrm -r /Users/caius/.gem/ruby/2.0.0?...y\nrm -r /Users/caius/.gem/ruby/2.1.7?...y\n\n$ ls ~/.gem/ruby\n2.3.1\n2.4.1\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd now revel in your reclaimed disk space. (Hunting other large folders/items on your disk? \u003ca href=\"http://dev.yorhel.nl/ncdu\"\u003e\u003ccode\u003encdu\u003c/code\u003e\u003c/a\u003e is a great tool for that.)\u003c/p\u003e\n"},{"title":"Something Goes Here","date_published":"2017-11-10T00:27:07Z","date_modified":"2017-11-10T00:27:07Z","id":"https://caiustheory.com/something-goes-here/","url":"https://caiustheory.com/something-goes-here/","author":{"name":"Caius Durling"},"content_html":"\u003c!-- raw HTML omitted --\u003e\n\u003cpre\u003e\u003ccode\u003eSomething goes here\nand something else\nover there slowly\nwrapping lines a\nlittle each time\noh no, you fucked\nup one little comma\nand now everything is running away drastically from you\nEnd it.\n\u003c/code\u003e\u003c/pre\u003e\n\u003c!-- raw HTML omitted --\u003e\n"},{"title":"git git git git git","date_published":"2017-09-26T11:00:23Z","date_modified":"2017-09-26T11:00:23Z","id":"https://caiustheory.com/git-git-git-git-git/","url":"https://caiustheory.com/git-git-git-git-git/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eEver found you\u0026rsquo;ve accidentally entered too many \u003ccode\u003egit\u003c/code\u003es in your terminal and wondered if there\u0026rsquo;s a solution to it? I quite often type \u003ccode\u003egit\u003c/code\u003e then go away and come back, then type a full \u003ccode\u003egit status\u003c/code\u003e after it. This leads to a lovely (annoying) error out the box:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ git git status\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit: \u003cspan class=\"s1\"\u003e\u0026#39;git\u0026#39;\u003c/span\u003e is not a git command. See \u003cspan class=\"s1\"\u003e\u0026#39;git --help\u0026#39;\u003c/span\u003e.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhat a git.\u003c/p\u003e\n\u003cp\u003eMy initial thought was overriding the \u003ccode\u003egit\u003c/code\u003e binary in my \u003ccode\u003e$PATH\u003c/code\u003e and having it strip any leading arguments that match \u003ccode\u003egit\u003c/code\u003e, so we end up running just the \u003ccode\u003egit status\u003c/code\u003e at the end of the arguments. An easier way is to just use \u003ca href=\"https://git-scm.com/docs/git-config\"\u003e\u003ccode\u003egit-config\u003c/code\u003e\u003c/a\u003e\u0026rsquo;s \u003ccode\u003ealias.*\u003c/code\u003e functionality to expand the first argument being \u003ccode\u003egit\u003c/code\u003e to a shell command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit config --global alias.git \u003cspan class=\"s1\"\u003e\u0026#39;!exec git\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhich adds the following git config to your \u003ccode\u003e.gitconfig\u003c/code\u003e file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[alias]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"na\"\u003egit\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e!exec git\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd then you\u0026rsquo;ll find you can \u003ccode\u003egit git\u003c/code\u003e to your heart\u0026rsquo;s content\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ git sha\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecc9c642663c0b63fba3964297c13ce9b61209313\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ git git sha\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecc9c642663c0b63fba3964297c13ce9b61209313\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ git git git git git git git git git git git git git git git git git git git git git git git git git git sha\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecc9c642663c0b63fba3964297c13ce9b61209313\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e(\u003ccode\u003egit sha\u003c/code\u003e is an alias for \u003ccode\u003egit rev-parse HEAD\u003c/code\u003e.)\u003c/p\u003e\n\u003cp\u003eSee what other git alias\u0026rsquo; I have in my \u003ca href=\"https://github.com/caius/zshrc/blob/master/dotfiles/gitconfig\"\u003e\u003ccode\u003e~/.gitconfig\u003c/code\u003e\u003c/a\u003e, and laugh at all the typo corrections I have in there. (Yes, git provides autocorrection if you enable it, but I\u0026rsquo;m used to these typos working!)\u003c/p\u003e\n\u003cp\u003eNow \u003ccode\u003egit\u003c/code\u003e back to doing useful things!\u003c/p\u003e\n"},{"title":"Upgrading Microserver G8 CPU","date_published":"2017-08-09T00:30:23Z","date_modified":"2017-08-09T00:30:23Z","id":"https://caiustheory.com/upgrading-microserver-g8-cpu/","url":"https://caiustheory.com/upgrading-microserver-g8-cpu/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eMy home server is a \u003ca href=\"https://www.hpe.com/uk/en/product-catalog/servers/proliant-servers/pip.hpe-proliant-microserver-gen8.5379860.html\"\u003eHP Proliant Microserver Gen 8\u003c/a\u003e, which is modestly powerful and runs everything I need at home in a fairly compact footprint without being too noisy or power hungry.\u003c/p\u003e\n\u003cp\u003eBoth the G8 and the previous revision, the G7, are moderately expandable in terms of memory \u0026amp; storage devices. (Not least of which is an internal USB port, which is useful plugging the SmartOS boot device into, no chance of it being knocked out!) I\u0026rsquo;ve upgraded the memory and HDDs in the time I\u0026rsquo;ve had the machine. (\u003ca href=\"/finding-cheap-microserver-g8-memory/\"\u003eHere\u0026rsquo;s how I upgraded the memory on the cheap\u003c/a\u003e!)\u003c/p\u003e\n\u003cp\u003eThe Gen 8 specifically is a little more upgradable however, as it comes with a 1155 CPU socket. Mine contained a \u003ca href=\"https://ark.intel.com/products/71074/Intel-Celeron-Processor-G1610T-2M-Cache-2_30-GHz\"\u003eCeleron G1610T CPU\u003c/a\u003e from new, which whilst quicker than the AMD one in the G7, was only two cores / two threads and not massively fast in the grand scheme of things.\u003c/p\u003e\n\u003cp\u003eGiven I run a few different things on the home server, it acts as a NAS for a handful of laptops, \u003ca href=\"https://www.plex.tv/\"\u003eplex server\u003c/a\u003e, \u003ca href=\"https://www.crashplan.com/\"\u003ecrashplan\u003c/a\u003e backup server, gathers various stats from network devices \u0026amp; runs the \u003ca href=\"https://unifi-hd.ubnt.com\"\u003eunifi controller\u003c/a\u003e. Mostly it\u0026rsquo;s fine, but transcoding in Plex specifically burns the CPU and on the odd occasion I\u0026rsquo;ve noticed it being slower than realtime and having to wait for the server to catch up.\u003c/p\u003e\n\u003cp\u003eSo what to do? As you\u0026rsquo;ve no doubt guessed from the title of the post, I upgraded the CPU in it. I did some research and found \u003ca href=\"https://b3n.org/installed-xeon-e3-1230v2-in-gen8-hp-microserver/\"\u003esomeone else who documented the upgrading process\u003c/a\u003e, as well as the \u003ca href=\"http://n40l.wikia.com/wiki/Cpu_gen8\"\u003eN40L wiki listing potential upgrade candidates\u003c/a\u003e. I then pulled together my own table of processors, specs \u0026amp; price on fleabay to work out which I want.\u003c/p\u003e\n\u003cp\u003eMy criteria were more than 2 cores/threads, ECC ram \u003cstrong\u003ehas\u003c/strong\u003e to be supported and ideally not much higher TDP than the stock CPU. \u003ca href=\"https://support.plex.tv/hc/en-us/articles/201774043\"\u003ePlex guidelines for CPU power\u003c/a\u003e state that as a (very) rough guideline, you need a 2000 PassMark score per 1080p transcode. Given I would like other things running alongside Plex, allowing 2-3x that figure sounds ideal.\u003c/p\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eCPU\u003c/th\u003e\n\u003cth\u003eCores\u003c/th\u003e\n\u003cth\u003eThreads\u003c/th\u003e\n\u003cth\u003eTDP (W)\u003c/th\u003e\n\u003cth\u003ePassmark\u003c/th\u003e\n\u003cth\u003ePrice (£)\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cem\u003eG1610T\u003c/em\u003e\u003c/td\u003e\n\u003ctd\u003e2\u003c/td\u003e\n\u003ctd\u003e2\u003c/td\u003e\n\u003ctd\u003e35\u003c/td\u003e\n\u003ctd\u003e2322\u003c/td\u003e\n\u003ctd\u003e12.00\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eE3-1220L v2\u003c/td\u003e\n\u003ctd\u003e2\u003c/td\u003e\n\u003ctd\u003e4\u003c/td\u003e\n\u003ctd\u003e17\u003c/td\u003e\n\u003ctd\u003e3701\u003c/td\u003e\n\u003ctd\u003e120.00\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eE3-1220L\u003c/td\u003e\n\u003ctd\u003e2\u003c/td\u003e\n\u003ctd\u003e4\u003c/td\u003e\n\u003ctd\u003e20\u003c/td\u003e\n\u003ctd\u003e3563\u003c/td\u003e\n\u003ctd\u003e60.00\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eE3-1260L\u003c/td\u003e\n\u003ctd\u003e4\u003c/td\u003e\n\u003ctd\u003e8\u003c/td\u003e\n\u003ctd\u003e45\u003c/td\u003e\n\u003ctd\u003e6534\u003c/td\u003e\n\u003ctd\u003e80.00\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eE3-1265L\u003c/td\u003e\n\u003ctd\u003e4\u003c/td\u003e\n\u003ctd\u003e8\u003c/td\u003e\n\u003ctd\u003e45\u003c/td\u003e\n\u003ctd\u003e6054\u003c/td\u003e\n\u003ctd\u003e64.00\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eE3-1265L v2\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003e4\u003c/td\u003e\n\u003ctd\u003e8\u003c/td\u003e\n\u003ctd\u003e45\u003c/td\u003e\n\u003ctd\u003e7733\u003c/td\u003e\n\u003ctd\u003e133.00\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003cem\u003eStock CPU is first in the list for comparison. Bold one is the chosen upgrade.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eAfter some deliberation, I decided to pick up the E3-1265L v2 from Ebay. It has a slightly higher TDP, but not so much that I\u0026rsquo;m worried about the temperature in the server. Most importantly it supports ECC ram \u0026amp; quadruples the physical cores, whilst providing a whopping eight threads for processing power. More than enough for a couple of concurrent Plex transcodes with cycles left over for other things at the same time.\u003c/p\u003e\n\u003cp\u003eI ordered it from a Hong Kong seller, and it arrived in the UK after 10 days or so as expected. It came with thermal paste, which I applied in a cross formation before refitting everything. The G8 is really easy to pull apart, HP really thought about that!\u003c/p\u003e\n\u003cp\u003eFirst boot with the processor went very smoothly, SmartOS recognises it quite happily.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e    [root@oscar ~]# sysinfo | grep -i cpu\n      \u0026quot;CPU Type\u0026quot;: \u0026quot;Intel(R) Xeon(R) CPU E3-1265L V2 @ 2.50GHz\u0026quot;,\n      \u0026quot;CPU Virtualization\u0026quot;: \u0026quot;vmx\u0026quot;,\n      \u0026quot;CPU Physical Cores\u0026quot;: 1,\n      \u0026quot;CPU Total Cores\u0026quot;: 8,\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eI\u0026rsquo;m also happy with the temperatures, even after streaming a couple of videos via plex for an hour or so, whilst backing up a laptop via crashplan, as well as the usual stuff that\u0026rsquo;s always running on the machine, everything was still well within normal ranges.\u003c/p\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eSensor\u003c/th\u003e\n\u003cth\u003eValue\u003c/th\u003e\n\u003cth\u003eUnits\u003c/th\u003e\n\u003cth\u003eState\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003eInlet Ambient\u003c/td\u003e\n\u003ctd\u003e22.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eCPU\u003c/td\u003e\n\u003ctd\u003e40.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eP1 DIMM 1-2\u003c/td\u003e\n\u003ctd\u003e40.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eChipset\u003c/td\u003e\n\u003ctd\u003e56.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eChipset Zone\u003c/td\u003e\n\u003ctd\u003e44.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eVR P1 Zone\u003c/td\u003e\n\u003ctd\u003e61.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eiLO Zone\u003c/td\u003e\n\u003ctd\u003e49.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003ePCI 1 Zone\u003c/td\u003e\n\u003ctd\u003e40.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSys Exhaust\u003c/td\u003e\n\u003ctd\u003e48.000\u003c/td\u003e\n\u003ctd\u003edegrees C\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eFan 1\u003c/td\u003e\n\u003ctd\u003e12.544\u003c/td\u003e\n\u003ctd\u003epercent\u003c/td\u003e\n\u003ctd\u003eok\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eAll in all, great success!\u003c/p\u003e\n\u003cp\u003e \n \u003c/p\u003e\n\u003cp\u003e\u003cem\u003eUpdated 2018-12-30: \u003ca href=\"https://twitter.com/tomwardill/\"\u003e@tomwardill\u003c/a\u003e \u003ca href=\"https://twitter.com/tomwardill/status/999002779887824896\"\u003enotes\u003c/a\u003e the E3-1230L V3 doesn\u0026rsquo;t fit: \u0026ldquo;The E3-1230L V3 that you list doesn\u0026rsquo;t fit, it\u0026rsquo;s a 1150 socket, not a 1155\u0026rdquo;. The table of candidates has been altered to remove it.\u003c/em\u003e\u003c/p\u003e\n"},{"title":"Wenlock Olympian Triathlon 2017","date_published":"2017-07-13T12:55:45Z","date_modified":"2017-07-13T12:55:45Z","id":"https://caiustheory.com/wenlock-olympian-triathlon-2017/","url":"https://caiustheory.com/wenlock-olympian-triathlon-2017/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSecond year in a row doing this Tri, \u003ca href=\"http://caiustheory.com/much-wenlock-triathlon-2016/\"\u003elast year\u003c/a\u003e I came away feeling like I hadn\u0026rsquo;t given it my best and wanted to return to complete unfinished business. \u003cem\u003e(Spoilers: I managed better this year.)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e500m Swim: 00:10:37\u003c/strong\u003e \u003cem\u003e(\u003ca href=\"https://www.strava.com/activities/1076088498\"\u003eStrava\u003c/a\u003e)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eStarted last in a lane of three, didn\u0026rsquo;t get overtaken which meant swimming at my own pace without being under pressure was easily achieved. Failed to exit the pool initially, dropped a handful of seconds there for sure.\u003c/p\u003e\n\u003cp\u003eDidn\u0026rsquo;t feel like I was pushing too hard, certainly felt energetic after leaving the building. Given my lack of swimming this year, I\u0026rsquo;m surprised at how well it went. Strength training over the winter months has definitely paid off more than I expected.\u003c/p\u003e\n\u003cp\u003eTook nearly a minute off my previous swim time, which is more than I would\u0026rsquo;ve expected. Was only +7 seconds off my estimated time when entering too!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e19km Cycle 00:45:44\u003c/strong\u003e \u003cem\u003e(\u003ca href=\"https://www.strava.com/activities/1076088548\"\u003eStrava\u003c/a\u003e)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eThe wonderful (👻) cycle route down and back up Wenlock Edge. Ran out of gear heading down the valley side (again), coasted most of the way down. Attempted to pace myself on the climbing, keeping an eye on my heart rate to know whether I could push harder or needed to ease off. Felt strong nearly all the way round, only came close to blowing on the final 15% climb (which was also nearer the end than I remembered/expected!)\u003c/p\u003e\n\u003cp\u003eGot off the bike with less left in my legs than I thought whilst I was in the saddle. Took a solid 11 minutes 10 seconds off my previous time (which did include walking to be fair - none of that this year) which I\u0026rsquo;m \u003cem\u003every\u003c/em\u003e happy with. Think I paced it nigh on perfectly on the day. Probably my strongest discipline currently too.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e7km Run 00:53:06\u003c/strong\u003e \u003cem\u003e(\u003ca href=\"https://www.strava.com/activities/1076088534\"\u003eStrava\u003c/a\u003e)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eStarted the run feeling like crap. First couple of km is a mostly flat trail run, where I didn\u0026rsquo;t push very hard and was basically attempting to find some energy from somewhere. Pretty sure this was a mental battle rather than physical, no cramps or super tired muscles.\u003c/p\u003e\n\u003cp\u003eA friend overtook me about 2.5km in and rather handily goaded me on by saying, \u0026ldquo;See you at the end!\u0026rdquo; as he went past (Thanks Paul!) which naturally made me kick harder and try to stick with him. Clung on to the halfway point (and the massive hill they make you climb just to turn round at the summit), and pulled ahead coming back down the route.\u003c/p\u003e\n\u003cp\u003eResorted to walking some sections of the run (probably about 750m in total), tailing and then dropping back from my mate in the last kilometre. (Dropped a minute in total to him on the run.) Technically I ran a 10k this season, but I\u0026rsquo;ve not run aside from that.\u003c/p\u003e\n\u003cp\u003e(Also binned my trainers upon returning home. They\u0026rsquo;ve finally given up after being beaten on pavements \u0026amp; woodland floors for about 50 miles.)\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTotal 01:49:27\u003c/strong\u003e \u003cem\u003e(Previously 02:14:40)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eCompleted in under two hours! Completely astounded at that, especially as I don\u0026rsquo;t \u003cem\u003efeel\u003c/em\u003e like I\u0026rsquo;ve been training much this year. (I have, but more socially than on a strict timetable and without doing many previous events this season.)\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;d told me at the end of last year that my second attempt would take ~25 minutes off my finish time, I\u0026rsquo;d have laughed at you.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://www.wenlock-olympian-society.org.uk/games/triathlon/\"\u003eWenlock Olympian Triathlon\u003c/a\u003e is definitely my favourite Triathlon, and also the most challenging I\u0026rsquo;ve done to date. I\u0026rsquo;m intending on doing it next year and seriously aiming to take at least another 10 minutes off my total time.\u003c/p\u003e\n"},{"title":"Raspberry Pi 3 as an emergency router","date_published":"2017-05-09T20:55:00Z","date_modified":"2017-05-09T20:55:00Z","id":"https://caiustheory.com/raspberry-pi-3-as-an-emergency-router/","url":"https://caiustheory.com/raspberry-pi-3-as-an-emergency-router/","content_html":"\u003cp\u003eGiven a dead router, how do you get back online whilst you wait for the replacement part to arrive? Grab a Raspberry Pi 3 off the shelf, along with a USB to Ethernet adapter and hey presto the internet works again.\u003c/p\u003e\n\u003cp\u003eThis is with a fibre modem (FTTC), using PPPoE to connect out. Plug the modem (WAN) into the RPi\u0026rsquo;s ethernet port, and plug the LAN switch into the USB adapter.\u003c/p\u003e\n\u003cp\u003eFirst thing is to get the WAN link working, get it talking PPPoE to the ISP. Usually this will be configured in \u003ccode\u003e/etc/ppp/pppoe.conf\u003c/code\u003e (depends on your linux distro). (That\u0026rsquo;ll require your username/password for your ISP usually too.)\u003c/p\u003e\n\u003cp\u003eGet it up \u0026amp; connected, and make sure you can ping the internet from the RPi. Then it\u0026rsquo;s time to get the LAN working. Give it a static IP in the range you want shared out.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# /etc/network/interfaces\niface eth0 inet static\n  address 192.168.1.1\n  netmask 255.255.255.0\n  gateway 192.168.1.1\n\nauto eth1\niface eth1 inet dhcp\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eGet a dhcp server running on the LAN connection,\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# /etc/dhcpcd.conf\ninterface eth0\nstatic ip_address=192.168.1.1\nstatic routers=192.168.1.1\nstatic domain_name_servers=8.8.8.8,8.8.4.4\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd then it\u0026rsquo;s time to handle WAN -\u0026gt; LAN traffic and the reverse. Make sure you have packet forwarding enabled, and then setup the firewall to handle NAT and also keep out undesirable traffic.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esysctl net.ipv4.ip_forward\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -F\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -X\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -t nat -F\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -t nat -X\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -i lo -j ACCEPT\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -i eth0 -j ACCEPT\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -p icmp --icmp-type any -j ACCEPT\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -f -j DROP\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eiptables -A INPUT -j DROP\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHey presto, you have a working emergency router. In testing I found my fibre connection (80/20Mb) was slower than the traffic the RPi could push, so didn\u0026rsquo;t notice any difference vs my normal router. (Although I did disable a bunch of automated stuff, so there was less contention on the WAN link.)\u003c/p\u003e\n"},{"title":"Finding cheap Microserver G8 memory","date_published":"2017-05-09T19:03:00Z","date_modified":"2017-05-09T19:03:00Z","id":"https://caiustheory.com/finding-cheap-microserver-g8-memory/","url":"https://caiustheory.com/finding-cheap-microserver-g8-memory/","content_html":"\u003cp\u003eI\u0026rsquo;ve been wanting to drop more memory in my \u003ca href=\"https://www.hpe.com/uk/en/product-catalog/servers/proliant-servers/pip.hpe-proliant-microserver-gen8.5379860.html\"\u003eHP Microserver G8\u003c/a\u003e, but hoping to find a cheaper alternative to buying new sticks from \u003ca href=\"http://uk.crucial.com/gbr/en/\"\u003eCrucial\u003c/a\u003e. I needed one or two 4GB sticks, but they had to be ECC of course.\u003c/p\u003e\n\u003cp\u003eAt the time of writing (May 2017), \u003ca href=\"http://uk.crucial.com/gbr/en/compatible-upgrade-for/HP-Compaq/proliant-microserver-gen8\"\u003eCrucial\u0026rsquo;s offering for the G8\u003c/a\u003e shows a 4GB stick to be £43.19, and an 8GB stick to be £81.59. This was a little more than I wanted to pay, but I was struggling to find anything on eBay or Amazon UK that I could be sure was ECC, and also cheaper.\u003c/p\u003e\n\u003cp\u003eEventually I wondered what else had compatible memory, after all this isn\u0026rsquo;t a bespoke machine. It should share the same memory specifications as plenty of other machines. The spec I was looking for was:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eDDR3 240-Pin UDIMM\u003c/li\u003e\n\u003cli\u003eECC\u003c/li\u003e\n\u003cli\u003e1600Mhz (or faster)\u003c/li\u003e\n\u003cli\u003e4 or 8GB sticks\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAfter a little while of searching, I happened to find the previous Mac Pro (ie. the tower, not the trashcan) also uses that specification of memory. One quick search on eBay and up turned someone selling off his 4GB sticks where he\u0026rsquo;d upgrade his Mac Pro to 8GB sticks across the board. £29 for 2x 4GB sticks is better than I was hoping for, and once fitted in the Microserver they work flawlessly.\u003c/p\u003e\n\u003cp\u003e(The onboard management software warns me that some processor features are disabled because I\u0026rsquo;m not using HP Approved memory, but it also logged that warning when I \u003cem\u003ewas\u003c/em\u003e using HP Approved memory previously and the machine worked perfectly then. No doubt it\u0026rsquo;s to make IT Managers who don\u0026rsquo;t like warnings spend more money with HP.)\u003c/p\u003e\n"},{"title":"Bash script setup","date_published":"2017-05-09T18:23:58Z","date_modified":"2017-05-09T18:23:58Z","id":"https://caiustheory.com/bash-script-setup/","url":"https://caiustheory.com/bash-script-setup/","content_html":"\u003cp\u003eRecently I\u0026rsquo;ve been writing a bunch of bash scripts for various things. As some up-front safety checks I\u0026rsquo;ve taken to opening every script with the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#!/usr/bin/env bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e[[\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$TRACE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e]]\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"nb\"\u003eset\u003c/span\u003e -o xtrace\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eset\u003c/span\u003e -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eset\u003c/span\u003e -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eset\u003c/span\u003e -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eset\u003c/span\u003e -o noclobber\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOther things I\u0026rsquo;m also trying to be good about doing:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eusing \u003ccode\u003ereadonly\u003c/code\u003e when declaring variables which shouldn\u0026rsquo;t be mutated\u003c/li\u003e\n\u003cli\u003eTrapping errors using an error function, and cleaning up anything temporary in there\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAnd some useful reading I ran across in my quest to level up bash-scripts:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://redsymbol.net/articles/bash-exit-traps/\"\u003eBash Exit Traps\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://redsymbol.net/articles/unofficial-bash-strict-mode/\"\u003eUse the unofficial Bash Strict Mode\u003c/a\u003e (Yes, \u003cem\u003etwo\u003c/em\u003e posts from \u003ca href=\"http://redsymbol.net/\"\u003ehttp://redsymbol.net/\u003c/a\u003e. Well worth reading.)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://jvns.ca/blog/2017/03/26/bash-quirks/\"\u003eBash scripting quirks \u0026amp; safety tips\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://wiki.bash-hackers.org/\"\u003eBash hackers wiki\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n"},{"title":"BMW 328i crankshaft sensor issue","date_published":"2017-04-21T22:18:00Z","date_modified":"2017-04-21T22:18:00Z","id":"https://caiustheory.com/bmw-328i-crankshaft-sensor-issue/","url":"https://caiustheory.com/bmw-328i-crankshaft-sensor-issue/","content_html":"\u003cp\u003eMy 1996 e36 BMW 328i Convertible was having trouble starting, and it\u0026rsquo;s always broken up \u003cem\u003eslightly\u003c/em\u003e at idle since I took ownership of the car. On a couple of hot days last summer it also stalled during idle, then started doing it again recently (even though the ambient temperature was much colder.)\u003c/p\u003e\n\u003cp\u003eThe main sympton at the point I took notice was it struggling to start, then hunting at idle until warm, then promptly stalling when warm and idling. It would also cut out when coming to a stop at junctions.\u003c/p\u003e\n\u003cp\u003eReading the codes showed one related to the crankshaft sensor:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e53 Crankshaft Sensor\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cem\u003e(Codes can also show up in hex, which would be \u0026ldquo;83 Crankshaft Sensor\u0026rdquo;.)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eI duly replaced both the Crankshaft Sensor (Part # 12141703277) and the Camshaft Sensor (Part # 12141703221), but it was still throwing the crankshaft sensor code.\u003c/p\u003e\n\u003cp\u003eUpon closer inspection, the sensor wire in the plug for the crankshaft sensor wasn\u0026rsquo;t pushed fully into the socket under the intake manifold. This lead to the ECU not being able to get a signal from the sensor, so it quite correctly threw a code and didn\u0026rsquo;t run correctly.\u003c/p\u003e\n\u003cp\u003eMade sure all the pins were seated in the plug correctly, and reconnected the plug under the intake manifold and she started up perfectly. Drives much better, and the engine pulls more smoothly all the way up the rev range.\u003c/p\u003e\n"},{"title":"Stop HealthKit causing SIGABRT","date_published":"2016-10-15T09:15:00Z","date_modified":"2016-10-15T09:15:00Z","id":"https://caiustheory.com/stop-healthkit-causing-sigabrt/","url":"https://caiustheory.com/stop-healthkit-causing-sigabrt/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eYou have some crazy idea for an iOS app that uses HealthKit so you fire up Xcode, create a new project \u0026amp; add the HealthKit entitlement. Follow the tutorial to request authorization from the \u003ccode\u003eHKHealthKitStore\u003c/code\u003e. Hit run to make sure the app compiles and find that it instantly crashes with a \u003ccode\u003eSIGABRT\u003c/code\u003e in \u003ccode\u003eAppDelegate\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003ePuzzled by this you go over the minimal amount of code you\u0026rsquo;ve added and pare it right down to just the \u003ccode\u003eHKHealthKitStore.requestAuthorization\u003c/code\u003e call which is still causing the \u003ccode\u003eSIGABRT\u003c/code\u003e as soon as the app tries to boot.\u003c/p\u003e\n\u003cp\u003eThe missing piece of the puzzle is \u003ccode\u003eInfo.plist\u003c/code\u003e needs a key adding to it for the HealthKit authorisation screen. The documentation helpfully forgets to mention this however. Here\u0026rsquo;s some quick simple steps to fix it:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eOpen \u003ccode\u003eInfo.plist\u003c/code\u003e in Xcode\u003c/li\u003e\n\u003cli\u003eClick the \u003ccode\u003e(+)\u003c/code\u003e at the top to add a new key/value to the file\u003c/li\u003e\n\u003cli\u003eEnter \u0026ldquo;Privacy - Health Share Usage Description\u0026rdquo; for the key\u003c/li\u003e\n\u003cli\u003eEnter a useful message to the user explaining why they should allow access to their healthkit data for your app for the value\u003c/li\u003e\n\u003cli\u003eRun your app and see the HealthKit authorisation sheet appear\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cstrong\u003eNB\u003c/strong\u003e: if you want to update/write any data to healthkit, you\u0026rsquo;ll need to add the \u0026ldquo;Privacy - Health Update Usage Description\u0026rdquo; key with a description as well.\u003c/p\u003e\n"},{"title":"Remove OS X Disk Password","date_published":"2016-09-16T10:27:00Z","date_modified":"2016-09-16T10:27:00Z","id":"https://caiustheory.com/remove-os-x-disk-password/","url":"https://caiustheory.com/remove-os-x-disk-password/","content_html":"\u003cp\u003eI recently reinstalled a laptop and in doing so setup full disk encryption in a slightly strange fashion. The basic flow I followed was:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eBoot into Recovery mode (hold ⌘-R at boot)\u003c/li\u003e\n\u003cli\u003eErase the internal HD as \u003ccode\u003eHFS+ (Journaled, Encrypted)\u003c/code\u003e and set a disk password\u003c/li\u003e\n\u003cli\u003eInstall OS X onto the internal disk\u003c/li\u003e\n\u003cli\u003eDuring setup, use Migration Assistant to copy clone containing previous install data from backup disk\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThis worked great in the end, once I\u0026rsquo;d recompiling various utilities I had installed. (Downside of moving from one CPU arch to another - can\u0026rsquo;t just copy all compiled binaries over.)\u003c/p\u003e\n\u003cp\u003eHowever, I failed at step 2 above and entered \u0026ldquo;password\u0026rdquo; as my disk password since it was only intended to be temporary. Usually OS X\u0026rsquo;s full disk encryption (FileVault 2) allows the machine users to unlock the disk, and not a standalone password. Due to the slightly odd way I setup the machine, I had the option of either using the disk password or my user account\u0026rsquo;s password.\u003c/p\u003e\n\u003cp\u003eHaving hunted around trying to find how to change or remove this disk password and leave only my users password, I finally stumbled across the magic incantations in an apple discussion thread asking \u003ca href=\"https://discussions.apple.com/thread/5105759?start=0\u0026amp;tstart=0\"\u003eHow to disable \u0026ldquo;Disk Password\u0026rdquo; on boot?\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe magic incantations are as follows:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eList all the passwords that can currently unlock the drive\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eMake sure there is a second password listed or removing the disk password will lock you out of the disk\u003c/strong\u003e.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ sudo fdesetup list -extended\n ESCROW  UUID                                                    TYPE USER\n         28376DDE-B6E1-48BE-A06F-4212067581D6    Disk Passphrase User\n         4DBF8CEF-40F7-4F00-902F-A47AA643C656                 OS User caius\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eNote the UUID of the \u0026ldquo;Disk Passphrase\u0026rdquo; entry, and remove that from the list\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e sudo fdesetup remove -uuid 28376DDE-B6E1-48BE-A06F-4212067581D6\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eList the passwords again to make sure the Disk Passphrase entry was removed\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ sudo fdesetup list -extended\n ESCROW  UUID                                                    TYPE USER\n         4DBF8CEF-40F7-4F00-902F-A47AA643C656                 OS User caius\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eHey presto, only your user is left being able to unlock the disk.\u003c/p\u003e\n"},{"title":"Find dependencies blocking rails upgrades","date_published":"2016-08-10T11:39:00Z","date_modified":"2016-08-10T11:39:00Z","id":"https://caiustheory.com/find-dependencies-blocking-rails-upgrades/","url":"https://caiustheory.com/find-dependencies-blocking-rails-upgrades/","content_html":"\u003cp\u003eThe initial pain point when upgrading a rails app is figuring out which of your dependencies are blocking you upgrading the actual \u003ccode\u003erails\u003c/code\u003e gem (\u0026amp; immediate dependencies, actionpack, etc.). One way to start this is to update the rails dependency in your \u003ccode\u003eGemfile\u003c/code\u003e and run \u003ccode\u003ebundle update rails\u003c/code\u003e. Then check the error output \u003cem\u003e(it never works first time…)\u003c/em\u003e to see which gems are blocking the upgrade. Repeat, rinse until it works.\u003c/p\u003e\n\u003cp\u003eI figured I\u0026rsquo;d cheat a little and eyeball the \u003ccode\u003eGemfile.lock\u003c/code\u003e to see which gems had an explicit dependency pinning rails (or actionpack, activejob, etc) to a version lower than I want to upgrade to, so I could get an idea of what needs to be upgraded without having to do them all one-by-one.\u003c/p\u003e\n\u003cp\u003eThen instead of eyeballing \u003ccode\u003eGemfile.lock\u003c/code\u003e, I wrote an awk script to pull out the interesting dependencies (ie, anything that depends on rails gems) so I just have to check which versions they depend on by hand.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-awk\" data-lang=\"awk\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Reads a Gemfile.lock and outputs all dependencies that depend on rails\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eBEGIN\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eparent_printed\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003erails_gems\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;^(rail(s|ties)|action(mailer|pack|view)|active(job|model|record|support))$\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# We only want the specs from the GEM section\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eNR\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e~\u003c/span\u003e \u003cspan class=\"sr\"\u003e/GEM/\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003enext\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eexit\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Skip parent gems we don\u0026#39;t care about (rails itself…)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"o\"\u003e~\u003c/span\u003e \u003cspan class=\"sr\"\u003e/^ {4}[^ ]/\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e~\u003c/span\u003e \u003cspan class=\"nx\"\u003erails_gems\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eparent_printed\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003enext\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Parent gems that aren\u0026#39;t part of rails core\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Store the name to be printed if we match below\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"o\"\u003e~\u003c/span\u003e \u003cspan class=\"sr\"\u003e/^ {4}[^ ]/\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eparent_printed\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003enext\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# If the nested gem (6 space prefix) matches rails-names and we have a parent value\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# set then we print them out - making sure to only print the parent once\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"o\"\u003e~\u003c/span\u003e \u003cspan class=\"sr\"\u003e/^ {6}[^ ]/\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e~\u003c/span\u003e \u003cspan class=\"nx\"\u003erails_gems\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eparent_printed\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eparent_printed\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eprint\u003c/span\u003e \u003cspan class=\"nx\"\u003eparent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eprint\u003c/span\u003e \u003cspan class=\"o\"\u003e$\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRun it against your \u003ccode\u003eGemfile.lock\u003c/code\u003e for the app you\u0026rsquo;re upgrading:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eawk -f rails5.awk Gemfile.lock\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd you\u0026rsquo;ll get output like this, to run through and see if any of the dependencies are pinning to lower versions than you need.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e    coffee-rails (4.0.1)\n      railties (\u0026gt;= 4.0.0, \u0026lt; 5.0)\n    factory_girl (4.4.0)\n      activesupport (\u0026gt;= 3.0.0)\n    factory_girl_rails (4.4.1)\n      railties (\u0026gt;= 3.0.0)\n    globalid (0.3.7)\n      activesupport (\u0026gt;= 4.1.0)\n    google-api-client (0.8.6)\n      activesupport (\u0026gt;= 3.2)\n    jquery-rails (3.1.4)\n      railties (\u0026gt;= 3.0, \u0026lt; 5.0)\n    jquery-ui-rails (5.0.5)\n      railties (\u0026gt;= 3.2.16)\n    rails-deprecated_sanitizer (1.0.3)\n      activesupport (\u0026gt;= 4.2.0.alpha)\n    rails-dom-testing (1.0.7)\n      activesupport (\u0026gt;= 4.2.0.beta, \u0026lt; 5.0)\n    rspec-rails (3.4.2)\n      actionpack (\u0026gt;= 3.0, \u0026lt; 4.3)\n      activesupport (\u0026gt;= 3.0, \u0026lt; 4.3)\n      railties (\u0026gt;= 3.0, \u0026lt; 4.3)\n    sass-rails (4.0.5)\n      railties (\u0026gt;= 4.0.0, \u0026lt; 5.0)\n    sprockets-rails (2.3.3)\n      actionpack (\u0026gt;= 3.0)\n      activesupport (\u0026gt;= 3.0)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn this case, I\u0026rsquo;m trying to take this app to rails 5.0, so all the ones specifying \u003ccode\u003e\u0026lt; 5\u003c/code\u003e and \u003ccode\u003e\u0026lt; 4.3\u003c/code\u003e need upgrading beforehand.\u003c/p\u003e\n"},{"title":"SmartOS Recovery mount /usbkey","date_published":"2016-07-25T17:59:00Z","date_modified":"2016-07-25T17:59:00Z","id":"https://caiustheory.com/smartos-recovery-mount-usbkey/","url":"https://caiustheory.com/smartos-recovery-mount-usbkey/","content_html":"\u003cp\u003eRecently I managed to hose a box in a perfectly self-inflicted storm of idiocy. Imagine a SmartOS server with the following issues:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eRoot password not noted down anywhere\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e/usbkey/config\u003c/code\u003e edited badly, meaning the network settings are wrong\u003c/li\u003e\n\u003cli\u003eRebooting the server to apply some other settings\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNeedless to say, this caused a tiny issue in the server doing what it\u0026rsquo;s supposed to. Luckily I had access to a KVM remote console for the box and the following worked.\u003c/p\u003e\n\u003cp\u003eI brought the machine up, choosing the second option for recovery at the grub menu. Waited for a login prompt, then logged in with \u003ccode\u003eroot\u003c/code\u003e/\u003ccode\u003eroot\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eRealised quite quickly that \u003ccode\u003e/usbkey\u003c/code\u003e must be persisted on the \u003ccode\u003ezones\u003c/code\u003e zfs pool otherwise the configuration would be lost after shutdown, so imported the correct pool, created a directory to mount into and then mounted the zfs share.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ezpool import zones\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir /usbkey\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emount -F zfs zones/usbkey /usbkey\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"Much Wenlock Triathlon 2016","date_published":"2016-07-10T16:00:00Z","date_modified":"2016-07-10T16:00:00Z","id":"https://caiustheory.com/much-wenlock-triathlon-2016/","url":"https://caiustheory.com/much-wenlock-triathlon-2016/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cstrong\u003e500m Pool Swim: 00:11:34\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eFelt good setting off, within a couple of lengths felt like I had no energy. Didn’t have enough to eat/drink during the morning before starting, really makes a huge difference once you set off. Must remember to replace my goggles at some point too, the strap comes loose without warning.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTransition 1: 00:03:06\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eRough time for transition. Legs felt good as soon as I was out the pool. Got my kit on without too much trouble, although taking a t-shirt style cycling top instead of a full zip jacket was a mistake—forgot I’d be putting it on wet. Had a gel in transition in the hope it would give me some energy.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e19km Cycle: 00:48:03\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eLovely start to the cycle route, massive downhill on recently resurfaced roads for the most part. Best part of the cycle ride, with the rest of the route consisting of climbing back up to the start. Couple of nasty steep hills I had to walk sections of, for all I fitted lower gears they still weren\u0026rsquo;t low enough given my lack of bike fitness currently.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTransition 2: 00:06:57\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBike racked easily, shoes swapped, gel necked and a couple of Lucozade bottles clutched and off to run. Took on another gel.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e7km Run: 01:04:37\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eNeeded all the fluid I carried, didn\u0026rsquo;t take anything else during the run and felt no less energetic than the other disciplines. Legs didn\u0026rsquo;t really hurt, just felt like lead and had no power. Route was basically a mini trail run then up a tarmac road to the turning point then head back via trail run to finish. Adopted a walk/run approach.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTotal: 02:14:17\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eCertainly the hardest triathlon I\u0026rsquo;ve done, and I\u0026rsquo;m probably at my least fittest compared to any of the others I\u0026rsquo;ve done to boot. The location was lovely, the weather was pretty decent (sunny but not too hot). Don\u0026rsquo;t feel like I was beaten by it, but definitely haven\u0026rsquo;t given it my best. One to redo next year and train towards.\u003c/p\u003e\n"},{"title":"Setup DHCP interface in FreeBSD","date_published":"2016-07-05T10:00:00Z","date_modified":"2016-07-05T10:00:00Z","id":"https://caiustheory.com/setup-dhcp-interface-in-freebsd/","url":"https://caiustheory.com/setup-dhcp-interface-in-freebsd/","content_html":"\u003cp\u003eGiven a FreeBSD instance without a configured network interface that you\u0026rsquo;d like to configure, first check what the name of the interface you want to configure is with \u003ccode\u003eifconfig\u003c/code\u003e. (Mine is \u003ccode\u003eem0\u003c/code\u003e in this instance.)\u003c/p\u003e\n\u003cp\u003eThen we need to add the configuration telling services that we want to use DHCP for this interface, and setting up our default router (use your IP, not mine!) too:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecat \u0026gt;\u0026gt; rc.conf \u003cspan class=\"s\"\u003e\u0026lt;\u0026lt;CONF\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003eifconfig_em0=\u0026#34;DHCP\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003edefault_router=\u0026#34;192.168.1.1\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003eCONF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd then we need to start \u003ccode\u003edhclient\u003c/code\u003e on the given interface:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eservice dhclient start em0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHey presto, you should see dhclient finding a DHCP server and being handed an IP address for \u003ccode\u003eem0\u003c/code\u003e.\u003c/p\u003e\n"},{"title":"SoundCloud RSS Feeds","date_published":"2015-11-26T23:33:37Z","date_modified":"2015-11-26T23:33:37Z","id":"https://caiustheory.com/soundcloud-rss-feeds/","url":"https://caiustheory.com/soundcloud-rss-feeds/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003ca href=\"https://soundcloud.com\"\u003eSoundCloud\u003c/a\u003e appears to have gained popularity in recent times for hosting podcasts on. As a consumer of their service they\u0026rsquo;re pretty good at everything except having a visible RSS feed on a profile page for a show! If I want to listen to a show in my podcast app of choice, an RSS feed is the easiest way for me to achieve that.\u003c/p\u003e\n\u003cp\u003eTurns out SoundCloud \u003cem\u003edo\u003c/em\u003e have RSS feeds, they\u0026rsquo;re just well hidden and unfindable from the profile page itself. Thankfully, you can construct the URL for it from information on the profile page, and here\u0026rsquo;s a bookmarklet that will do it for you:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ejavascript:var%20userURI;var%20metaTags=document.getElementsByTagName(\n%22meta%22);for(var%20i=0;i\u0026lt;metaTags.length;i++){t=metaTags[i];if(\nt.attributes[%22property%22]\u0026amp;\u0026amp;t.attributes[%22property%22].value==\n%22al:ios:url%22){userURI=t.content;}}if(userURI){u=userURI.split(%22//%22)[1];\nwindow.location=%22http://feeds.soundcloud.com/users/soundcloud:%22+u+\n%22/sounds.rss%22;}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eOr as a \u003ca href=\"\"\u003ehandy link\u003c/a\u003e to copy to your bookmarks bar. Simply click/run that when on a SoundCloud profile page and you\u0026rsquo;ll be taken to the RSS Feed URL.\u003c/p\u003e\n"},{"title":"exec(3) in Go","date_published":"2015-01-31T09:37:37Z","date_modified":"2015-01-31T09:37:37Z","id":"https://caiustheory.com/exec-3-in-go/","url":"https://caiustheory.com/exec-3-in-go/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eUsing \u003ca href=\"http://man7.org/linux/man-pages/man3/exec.3.html\"\u003eexec(3)\u003c/a\u003e from Go is simple enough, once you figure out to look in the \u003ca href=\"http://golang.org/pkg/syscall/\"\u003esyscall\u003c/a\u003e package and how to pass arguments to the new command.\u003c/p\u003e\n\u003cp\u003eAs a simple example, I\u0026rsquo;m going to exec \u003ccode\u003e/bin/echo\u003c/code\u003e with a hardcoded string from the go binary. \u003cem\u003eThe program built here is in the \u003ca href=\"https://github.com/caius/gecho\"\u003egecho\u003c/a\u003e (Gecko, geddit?) git repo, which each stage as a commit.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eIn our main function lets setup some variables we\u0026rsquo;re going to need for arguments to \u003ccode\u003esyscall.Exec\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ecmdPath\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;/bin/echo\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ecmdArgs\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;World\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ecmdEnv\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e{}\u003c/span\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003e(We could use \u003ccode\u003eos.Environ()\u003c/code\u003e for \u003ccode\u003ecmdEnv\u003c/code\u003e to take the ENV from the go binary, but we don\u0026rsquo;t require anything from the environmnt here so it doesn\u0026rsquo;t matter that we aren\u0026rsquo;t.)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eNow we have the arguments for \u003ccode\u003esyscall.Exec\u003c/code\u003e, lets add that in and see what happens:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003esyscall\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eExec\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ecmdPath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ecmdArgs\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ecmdEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003epanic\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eerr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd running the file (\u003ccode\u003ego run gecho.go\u003c/code\u003e compiles \u0026amp; runs for us) gives the following output:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eWorld\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eErr, say what now? Where\u0026rsquo;s \u0026ldquo;Hello\u0026rdquo; gone?!\u003c/p\u003e\n\u003cp\u003eTook me a while to figure this out when I originally ran into this. The answer is staring us right in the face if we go look at the \u003ca href=\"http://golang.org/pkg/syscall/#Exec\"\u003esyscall.Exec docs\u003c/a\u003e. Lets have a look at the function signature, argument names and all:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eExec\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eargv0\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eargv\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eenvv\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"kt\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHmm. The first argument is \u003ccode\u003eargv0\u003c/code\u003e (and a string), rather than \u003ccode\u003ebinaryPath\u003c/code\u003e or something similar. The second argument is then \u003ccode\u003eargv\u003c/code\u003e and an array of strings.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eAt this point I remember that the first element of \u003ccode\u003eargv\u003c/code\u003e in other runtimes is the name of the binary or command invoked - \u003ccode\u003e$0\u003c/code\u003e in a bash script is the name of the script for example.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eThe answer is simple. \u003ccode\u003ecmdArgs\u003c/code\u003e in our script should have \u003ccode\u003e/bin/echo\u003c/code\u003e as the first element, and then we pass \u003ccode\u003ecmdArgs[0], cmdArgs\u003c/code\u003e as the first two arguments to \u003ccode\u003esyscall.Exec\u003c/code\u003e. Lets give that a go:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ecmdArgs\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/bin/echo\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;World\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ecmdEnv\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003esyscall\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eExec\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ecmdArgs\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"nx\"\u003ecmdArgs\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ecmdEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003epanic\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eerr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd running it (\u003ccode\u003ego run gecho.go\u003c/code\u003e remember) gives the expected output:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eHello World\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eExcellent. Now I just need to remember \u003ccode\u003eargv\u003c/code\u003e contains the command name as \u003ccode\u003eargv[0]\u003c/code\u003e and we\u0026rsquo;re golden.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eThere is also the \u003ca href=\"http://golang.org/pkg/os/exec/\"\u003eos/exec\u003c/a\u003e package in the stdlib, which is intended for executing other binaries as child processes from what I can tell. Tellingly, when you create a \u003ccode\u003eexec.Cmd\u003c/code\u003e struct with \u003ccode\u003eexec.Command()\u003c/code\u003e you give it the name as first argument, and args as following arguments. Then it has the following snippet in the documentation:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe returned Cmd\u0026rsquo;s Args field is constructed from the command name followed by the elements of arg, so arg should not include the command name itself. For example, \u003ccode\u003eCommand(\u0026quot;echo\u0026quot;, \u0026quot;hello\u0026quot;)\u003c/code\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eSo \u003ccode\u003ecmd := exec.Command(\u0026quot;echo\u0026quot;, \u0026quot;hello\u0026quot;); cmd.Args\u003c/code\u003e would return \u003ccode\u003e[]string{\u0026quot;echo\u0026quot;, \u0026quot;hello\u0026quot;}\u003c/code\u003e - which is recognisable as what we have to pass to \u003ccode\u003esyscall.Exec\u003c/code\u003e!\u003c/p\u003e\n"},{"title":"Changing hostname in SmartOS Zone","date_published":"2014-12-18T10:00:00Z","date_modified":"2014-12-18T10:00:00Z","id":"https://caiustheory.com/changing-hostname-in-smartos-zone/","url":"https://caiustheory.com/changing-hostname-in-smartos-zone/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eGiven a non-global zone in SmartOS that we want to change the hostname of, we need to make sure to edit the following files to change it:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e/etc/hosts\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e/etc/nodename\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eA quick way to do that is with \u003ccode\u003esed\u003c/code\u003e \u003cem\u003e(renaming \u0026ldquo;fred\u0026rdquo; to \u0026ldquo;beth\u0026rdquo; here)\u003c/em\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esed -e \u003cspan class=\"s1\"\u003e\u0026#39;s/fred/beth/g\u0026#39;\u003c/span\u003e -i /etc/hosts /etc/nodename\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen shutdown \u0026amp; start the zone \u003cem\u003e(from my testing a restart doesn\u0026rsquo;t apply it)\u003c/em\u003e.\u003c/p\u003e\n"},{"title":"Install Rubinius on OS X","date_published":"2014-11-05T12:50:16Z","date_modified":"2014-11-05T12:50:16Z","id":"https://caiustheory.com/install-rubinius-on-os-x/","url":"https://caiustheory.com/install-rubinius-on-os-x/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eUsing \u003ca href=\"https://github.com/postmodern/ruby-install/\"\u003eruby-install\u003c/a\u003e, \u003ca href=\"http://brew.sh/\"\u003ehomebrew\u003c/a\u003e building it for use with \u003ca href=\"https://github.com/postmodern/chruby/\"\u003echruby\u003c/a\u003e, here\u0026rsquo;s how I install \u003ca href=\"http://rubini.us/\"\u003eRubinius\u003c/a\u003e under Yosemite (works for Mavericks as well.)\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eMake sure llvm is installed\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ brew install llvm\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003ePrepend the homebrew-installed llvm tools to your path\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ export PATH=\u0026quot;$(brew --prefix llvm)/bin:$PATH\u0026quot;\n\n # Or, for ZSH\n $ path=( $(brew --prefix llvm)/bin $path )\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eInstall rubinius, v2.3.0 at the time of writing\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ ruby-install rbx 2.3.0\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eOpen a fresh shell once that\u0026rsquo;s built, and you should be able to switch to rbx!\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ chruby rbx\n $ ruby -v\n rubinius 2.3.0 (2.1.0 9d61df5d 2014-10-31 3.5.0 JI) [x86_64-darwin14.0.0]\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cem\u003eThere is also a homebrew tap for rubinius which should also work instead of the above. I couldn\u0026rsquo;t get it working on one of my laptops though, which is why I was installing by hand using the above instead. The tap is at \u003ca href=\"https://github.com/rubinius/homebrew-apps/\"\u003ehttps://github.com/rubinius/homebrew-apps/\u003c/a\u003e and \u003ca href=\"https://twitter.com/brixen/status/529725881498226688\"\u003ehttps://twitter.com/brixen/status/529725881498226688\u003c/a\u003e explains install steps.\u003c/em\u003e\u003c/p\u003e\n"},{"title":"Changing root password in global zone","date_published":"2014-10-19T17:44:20Z","date_modified":"2014-10-19T17:44:20Z","id":"https://caiustheory.com/changing-root-password-in-global-zone/","url":"https://caiustheory.com/changing-root-password-in-global-zone/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSmartOS mounts \u003ccode\u003e/etc/shadow\u003c/code\u003e from \u003ccode\u003e/usbkey/shadow\u003c/code\u003e so we can change the root password for the global zone after install. Here\u0026rsquo;s how:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eFire up a console or ssh session as root in the global zone\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eCheck the existing permissions on the file\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ ls -l /usbkey/shadow\n -r--------   1 root     root         560 Oct 19 16:45 /usbkey/shadow\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eMake the file writable\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ chmod 600 /usbkey/shadow\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFire up \u003ccode\u003evi\u003c/code\u003e to edit the file\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ vi /usbkey/shadow\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eEdit the line containing root to change the crypted password. See \u003ca href=\"https://us-east.manta.joyent.com/smartosman/public/man4/shadow.4.html\"\u003e\u003ccode\u003eshadow(4)\u003c/code\u003e\u003c/a\u003e if you need help with the format of \u003ccode\u003e/etc/shadow\u003c/code\u003e \u0026amp; use \u003ccode\u003e/usr/lib/cryptpass\u003c/code\u003e to generate a hash for the password you desire. \u003cem\u003e(Remember to clean the bash history!)\u003c/em\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eSave the file and exit \u003ccode\u003evi\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eMake the file readonly again\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ chmod 400 /usbkey/shadow\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eDouble check permissions are correct on the file again\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ ls -l /usbkey/shadow\n -r--------   1 root     root         560 Oct 19 16:49 /usbkey/shadow\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eJob done. Verify by logging in as root (invoking \u003ccode\u003e/usr/bin/login\u003c/code\u003e from an ssh session makes this easy to verify.)\u003c/p\u003e\n"},{"title":"Compiling SmartOS for AMD processors","date_published":"2014-09-28T10:00:00Z","date_modified":"2014-09-28T10:00:00Z","id":"https://caiustheory.com/compiling-smartos-for-amd-processors/","url":"https://caiustheory.com/compiling-smartos-for-amd-processors/","content_html":"\u003cp\u003eThere\u0026rsquo;s a few community-provided patches for SmartOS that enable KVM on AMD processors amongst other things, and given the HP Microserver has an AMD processor, that\u0026rsquo;s quite useful for turning it into a better lab server. The main \u003ca href=\"http://imgapi.uqcloud.net/builds\"\u003elist of so called \u0026ldquo;eait\u0026rdquo; builds\u003c/a\u003e was hiccuping when I tried to download the latest, and all I could find was a 20140812T062241Z image \u003ca href=\"http://builds.smartos.skylime.net\"\u003ehere\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe source code for the eait builds is maintained at \u003ca href=\"https://github.com/arekinath/smartos-live\"\u003ehttps://github.com/arekinath/smartos-live\u003c/a\u003e, and you can see the patches applied on top of the normal SmartOS master by going to \u003ca href=\"https://github.com/arekinath/smartos-live/compare/joyent:master...eait\"\u003ehttps://github.com/arekinath/smartos-live/compare/joyent:master...eait\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eSo here\u0026rsquo;s how to use SmartOS to compile a more up to date AMD-friendly Smartos!\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eGrab the latest multiarch SmartOS image (which \u003cstrong\u003ehas\u003c/strong\u003e to be used, or the compile will fail.) The latest at the time of writing was \u003ccode\u003e4aec529c-55f9-11e3-868e-a37707fcbe86\u003c/code\u003e, so that\u0026rsquo;s what I\u0026rsquo;ll use.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e imgadm import 4aec529c-55f9-11e3-868e-a37707fcbe86\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eSpin up a zone for us to build in (the \u003ca href=\"http://wiki.smartos.org/display/DOC/Building+SmartOS+on+SmartOS\"\u003eBuilding SmartOS on SmartOS\u003c/a\u003e page has extra info about this):\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e echo '{\n   \u0026quot;alias\u0026quot;: \u0026quot;platform-builder\u0026quot;,\n   \u0026quot;brand\u0026quot;: \u0026quot;joyent\u0026quot;,\n   \u0026quot;dataset_uuid\u0026quot;: \u0026quot;4aec529c-55f9-11e3-868e-a37707fcbe86\u0026quot;,\n   \u0026quot;max_physical_memory\u0026quot;: 32768,\n   \u0026quot;quota\u0026quot;: 0,\n   \u0026quot;tmpfs\u0026quot;: 8192,\n   \u0026quot;fs_allowed\u0026quot;: \u0026quot;ufs,pcfs,tmpfs\u0026quot;,\n   \u0026quot;maintain_resolvers\u0026quot;: true,\n   \u0026quot;resolvers\u0026quot;: [\n     \u0026quot;8.8.8.8\u0026quot;,\n     \u0026quot;8.8.4.4\u0026quot;\n   ],\n   \u0026quot;nics\u0026quot;: [\n     {\n       \u0026quot;nic_tag\u0026quot;: \u0026quot;admin\u0026quot;,\n       \u0026quot;ip\u0026quot;: \u0026quot;dhcp\u0026quot;,\n       \u0026quot;primary\u0026quot;: true\n     }\n   ],\n   \u0026quot;internal_metadata\u0026quot;: {\n     \u0026quot;root_pw\u0026quot;: \u0026quot;password\u0026quot;,\n     \u0026quot;admin_pw\u0026quot;: \u0026quot;password\u0026quot;\n   }\n }' | vmadm create\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eLogin to the created zone:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e zlogin \u0026lt;uuid from `vmadm create` output\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eUpdate the image to the latest packages, etc:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e pkgin -y update \u0026amp;\u0026amp; pkgin -y full-upgrade\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eInstall a few images we\u0026rsquo;ll need to compile \u0026amp; package SmartOS:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e pkgin install scmgit cdrtools pbzip2\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eGrab the source code of the fork containing the patches we want, from \u003ca href=\"https://github.com/arekinath/smartos-live\"\u003earekinath/smartos-live\u003c/a\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e git clone https://github.com/arekinath/smartos-live\n cd smartos-live\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cem\u003eOptional\u003c/em\u003e: Edit \u003ccode\u003esrc/Makefile.defs\u003c/code\u003e and change \u003ccode\u003ePARALLEL =      -j$(MAX_JOBS)\u003c/code\u003e to \u003ccode\u003ePARALLEL =      -j8\u003c/code\u003e to do less at once. (Microserver only has a dual core CPU!)\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eCopy the configure definition into the right place and start configuration:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e cp {sample.,}configure.smartos\n ./configure\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cem\u003e(You\u0026rsquo;ll probably get asked to accept the java license during configuration, so keep half an eye on it)\u003c/em\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eOnce configure has completed (which doesn\u0026rsquo;t take \u003cem\u003etoo\u003c/em\u003e long, 15 minutes or so), start building:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e gmake world \u0026amp;\u0026amp; gmake live\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eOnce the build is successfully finished, time to package an iso \u0026amp; usb image:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eexport LC_ALL=C\ntools/build_iso\ntools/build_usb\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eHey presto, you\u0026rsquo;ve a freshly built AMD-friendly SmartOS build to flash to a USB key / put on your netboot server and boot your Microserver from!\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eReferences\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://wiki.smartos.org/display/DOC/Building+SmartOS+on+SmartOS\"\u003ehttp://wiki.smartos.org/display/DOC/Building+SmartOS+on+SmartOS\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/arekinath/smartos-live\"\u003ehttps://github.com/arekinath/smartos-live\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n"},{"title":"The People's Triathlon 2014","date_published":"2014-08-11T10:00:00Z","date_modified":"2014-08-11T10:00:00Z","id":"https://caiustheory.com/the-peoples-triathlon-2014/","url":"https://caiustheory.com/the-peoples-triathlon-2014/","content_html":"\u003cp\u003eMy first Olympic distance triathlon, and the first time I\u0026rsquo;ve ever run 10km to boot.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1500m Open Water (Freshwater Lake) Swim: 00:38:08\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eEqually happy and upset with my swim. Basically everything went well except my wetsuit, must get a proper swimming one (possibly sleeveless) before my next wetsuit event. Having to get out at the end of each lap and run down the bank to re-enter the water was a bit strange but not too bad. Worst bit of that was diving back in and forgetting to look down - goggles bruised the edge of my eyesocket!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTransition 1: 00:03:53\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eTook this at a sedate pace, didn\u0026rsquo;t rush but didn\u0026rsquo;t dawdle either. Perhaps could\u0026rsquo;ve gone a bit quicker but not unhappy with it. Found bike OK this time, although chain fell off as I went to mount which was slightly annoying.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e40km Cycle: 01:28:49\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eStarted off really badly, managed to knock my watch from cycle into Transition 2 before I\u0026rsquo;d gotten out the car park. Had to reset it quickly into just bike mode from multisport mode which wasn\u0026rsquo;t too bad to be fair, just annoying. Having done the route in training was a \u003cem\u003ebig\u003c/em\u003e help on the day, I was aiming for about the time I did it in. Didn\u0026rsquo;t push too hard on the two hills (one of which looks deceptively easy). Very happy with my time and more importantly how little effort it was comparatively.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTransition 2: 00:01:28\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eQuicker than T1, as normal. Got my shit on quickly, didn\u0026rsquo;t forget anything and even managed to get my watch in run mode whilst jogging to the exit of the transition area.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e10km Run: 01:18:40\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe bit I was dreading the most. Very very happy with my performance here though. Walked about 90 seconds of the whole thing and kept plodding away the rest of the time. Given the furthest I\u0026rsquo;d run solidly in training was about 5.5km, I was exceptionally happy to just keep plodding the whole time. Managed to not take on liquid for the third lap, which lead to some impressive cramping in my right quad, so making sure I take liquid on is a definite thing to watch out for in future. And amazingly I wasn\u0026rsquo;t even tail end charlie (not that it really matters, given I race the clock, not other people. But still.)\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTotal: 03:30:51\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThere\u0026rsquo;s a tiny part of me that\u0026rsquo;s gutted I didn\u0026rsquo;t manage it in under 3:30 hours, but what\u0026rsquo;s 51 seconds over that period of time. More importantly, I completed the bloody thing! Especially happy with the run, enjoyed the cycle even if it was trying to drown us (there was 6\u0026quot; deep standing water on one of the roundabouts by the end), and mostly happy with my swim.\u003c/p\u003e\n\u003cp\u003eLooking forward to returning next year and beating 3:30!\u003c/p\u003e\n"},{"title":"Solve volume down button not working on iPhone 5","date_published":"2014-07-01T11:31:39Z","date_modified":"2014-07-01T11:31:39Z","id":"https://caiustheory.com/solve-volume-down-button-not-working-on-iphone-5/","url":"https://caiustheory.com/solve-volume-down-button-not-working-on-iphone-5/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI noticed this morning that my volume down button (-) wasn\u0026rsquo;t working on my iPhone 5 running iOS 7. Pushing the physical button in didn\u0026rsquo;t change the volume. The volume up button increased the volume successfully still.\u003c/p\u003e\n\u003cp\u003eAs is my normal first step debugging iPhone weirdness, I rebooted the phone by turning it off, leaving it off for a few seconds, then booting it back up with the power button. Once powered off and on in this way, the volume down key still didn\u0026rsquo;t decrease the volume.\u003c/p\u003e\n\u003cp\u003eFearing a physical button issue at this point, I turned to google for suggestions on what else to try. Running across \u003ca href=\"https://discussions.apple.com/thread/4894152\"\u003ethis thread\u003c/a\u003e on Apple\u0026rsquo;s discussion forums, I tried out the solution in there.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eOpen \u0026ldquo;Settings\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eScroll down and tap on \u0026ldquo;General\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eTap on \u0026ldquo;Accessibility\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eScroll down to the bottom and tap on \u0026ldquo;AssistiveTouch\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eTap the toggle for AssistiveTouch to turn it on, and you should see a little icon appear on screen (white circle contained in a dark grey rounded square)\u003c/li\u003e\n\u003cli\u003eTap the AssistiveTouch icon (was in the top left corner on screen for me)\u003c/li\u003e\n\u003cli\u003eTap on \u0026ldquo;Device\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eTap \u0026ldquo;Volume Down\u0026rdquo; a bunch of times and you should see the volume being turned down\u003c/li\u003e\n\u003cli\u003eTap outside the AssistiveTouch dialog to close it\u003c/li\u003e\n\u003cli\u003eTry pushing the physical Volume Down button\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIn my case, following these steps made my physical volume down button start working again. Makes me wonder if the solution author on the apple discussion thread is right, in that this is a software issue and forcing a volume down action through the on-screen interface makes it remember that there\u0026rsquo;s a physical button to respond to as well.\u003c/p\u003e\n\u003cp\u003eEither way, I can stop deafening myself whenever I receive a notification now!\u003c/p\u003e\n"},{"title":"Compile \u0026 run swift files directly","date_published":"2014-06-06T14:32:18Z","date_modified":"2014-06-06T14:32:18Z","id":"https://caiustheory.com/compile-run-swift-files-directly/","url":"https://caiustheory.com/compile-run-swift-files-directly/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eTurns out you can run a swift file without having to compile it into a binary somewhere and then run that binary. Makes swift behave a bit more like a scripting language like ruby or python when you need it to.\u003c/p\u003e\n\u003cp\u003eUsing the \u003ccode\u003excrun\u003c/code\u003e binary, we can reach into the current Xcode \u003ccode\u003e/bin\u003c/code\u003e folder and run binaries within there. So \u003ccode\u003excrun swift\u003c/code\u003e lets you run the swift binary to compile files for instance. If you view the help with \u003ccode\u003e-h\u003c/code\u003e, there\u0026rsquo;s a useful flag \u003ccode\u003e-i\u003c/code\u003e listed there:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e-i    Immediate mode\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eTurns out immediate mode means \u0026ldquo;compile \u0026amp; run\u0026rdquo; in the same command, which is what we\u0026rsquo;re after.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ cat hello.swift\nprintln(\u0026quot;Hello World\u0026quot;)\n\n$ xcrun swift -i hello.swift\nHello World\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eBingo. But what if we want to make hello.swift executable and call it directly without having to know it needs the swift binary to call it. Unix lets files define their shebang to say how the file needs to be executed, so lets go for that here too!\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ cat hello2.swift\n#!/usr/bin/env xcrun swift -i\nprintln(\u0026quot;Hello World 2\u0026quot;)\n\n$ chmod +x hello2.swift\n$ ./hello2.swift\nHello World 2\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNo more having to fire up Xcode for quick CLI tools, especially ones using the system frameworks!\u003c/p\u003e\n"},{"title":"Cheshire Triathlon 2014","date_published":"2014-06-02T10:00:00Z","date_modified":"2014-06-02T10:00:00Z","id":"https://caiustheory.com/cheshire-triathlon-2014/","url":"https://caiustheory.com/cheshire-triathlon-2014/","content_html":"\u003cp\u003eSuccessfully completed my second Sprint Triathlon of the season in Nantwich.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e500m Swim - 00:10:49\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eFelt good swimming this. Mixed up my usual breaststroke with some front crawl to overtake and use my legs less towards the end. Think I need to put a shorter time down on paper next time though, was in with people who were swimming much slower than me. (Including one chap who was doing doggy paddle in the deep end and walking as soon as his feet touched bottom. Oh well!) Need to continue with front crawl practice, and aim to do next sprint tri entirely crawl.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTransition 1 - 00:02:43\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWent well. Ran from pool to bike and felt OK. Kit on without any fuckups. Didn\u0026rsquo;t fall off getting on bike. Winning. Need to practice getting on my bike in shoes at a run though.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e20km Bike - 00:42:31\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e10 minutes quicker than same length course last year. (Although a slightly new route this year.) Lost a minute when my water bottle fell out about 12km in though. Barely saw a soul, got stuck at one pedestrian crossing on red. Felt very good on the bike (and finally beat Liam on the same route \u0026amp; day  ). Need to fit a tighter bottle carrier to stop the bottle falling out on bumpy roads.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTransition 2 - 00:01:21\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eCame off bike well and ran to shoes. Couldn\u0026rsquo;t find where I\u0026rsquo;d left them, was trying to find Liam\u0026rsquo;s bike as my marker for it, only someone else earlier in the same row had the same bike as him! Wasted 10 seconds looking probably. Need to remember where my stuff is more accurately in future.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e5km Run - 00:39:03\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBoiling hot by the point I started running; didn\u0026rsquo;t feel great in the first few hundred meters and never really got comfortable after that. Walked about 800m of it in total at a guess, really didn\u0026rsquo;t have much left in my legs. Not convinced pushing less on the bike would\u0026rsquo;ve helped my run though. On the plus side, only 7 minutes slower than a training run a couple of weeks ago in similar heat, and this was on grass. Need to continue running at 5km distance, and train on grass as well as tarmac I think.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTotal time - 01:36:27\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e8 minutes slower than York a couple of months ago, and felt \u003cem\u003emuch\u003c/em\u003e worse on the run. Very happy with my bike \u0026amp; swim though. More run training required!\u003c/p\u003e\n"},{"title":"York April 2014 Triathlon","date_published":"2014-04-28T10:00:00Z","date_modified":"2014-04-28T10:00:00Z","id":"https://caiustheory.com/york-triathlon-2014/","url":"https://caiustheory.com/york-triathlon-2014/","content_html":"\u003cp\u003eCompleted the York sprint distance tri in 1:28:37 total, and didn\u0026rsquo;t die in the process. Split times \u0026amp; my thoughts below..\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e400m Swim: 00:08:08\u003c/strong\u003e\u003cbr\u003e\n\u003cstrong\u003eT1: 00:03:47\u003c/strong\u003e\u003cbr\u003e\n\u003cstrong\u003e18km Bike: 00:38:43\u003c/strong\u003e\u003cbr\u003e\n\u003cstrong\u003eT2: 00:00:58\u003c/strong\u003e\u003cbr\u003e\n\u003cstrong\u003e5km Run: 00:38:08\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e(Amusingly the official time for my swim was 00:10:10, which then makes my official time for T1 00:00:38; Fastest Swim/Bike transition time in the tri on paper! Ahem.)\u003c/p\u003e\n\u003cp\u003eHappy with my swim, not much different from training speed \u0026amp; felt good getting out the pool. T1 was okay, and I remembered to put my tshirt on before my helmet this time! Quite happy with my cycling, got down on the drop bars going downhill with tailwind, and could maintain 16mph uphill on the return leg, the bike itself had a major service 2 weeks ago and I\u0026rsquo;d not really ridden much since that either. Legs responded with life in them coming off the bike, second transition was quicker than expected (elastic laces win!). Moderately happy with the run, was hoping to do it in under 40 minutes (and did), but still felt like I had no more distance/pace left before halfway through. Pushed through to the end okay, and didn\u0026rsquo;t walk at all. Overall very happy for my first sprint distance, and especially happy to be running 5km after swim/bike without walking.\u003c/p\u003e\n\u003cp\u003eFirst event of the season done. Main area to work on is my run, but that\u0026rsquo;s been the case since last season. Winter running training seems to have paid off so far.\u003c/p\u003e\n"},{"title":"Rename Google Drive folder on Mac","date_published":"2014-03-06T15:49:22Z","date_modified":"2014-03-06T15:49:22Z","id":"https://caiustheory.com/rename-google-drive-folder-on-mac/","url":"https://caiustheory.com/rename-google-drive-folder-on-mac/","author":{"name":"Caius Durling"},"content_html":"\u003col\u003e\n\u003cli\u003eQuit Google Drive app\u003c/li\u003e\n\u003cli\u003eRename \u003ccode\u003e~/Google Drive/\u003c/code\u003e to whatever you want\u003c/li\u003e\n\u003cli\u003eOpen Google Drive and wait for it to complain the folder is missing\u003c/li\u003e\n\u003cli\u003eClick the menu item and click the warning item\u003c/li\u003e\n\u003cli\u003eClick \u0026ldquo;Locate Folder..\u0026rdquo; button on the right in window that pops up\u003c/li\u003e\n\u003cli\u003eFind your renamed folder and hit OK\u003c/li\u003e\n\u003cli\u003eThere is no step seven\u003c/li\u003e\n\u003c/ol\u003e\n"},{"title":"Add to iCloud Reading List programmatically","date_published":"2013-12-16T22:26:31Z","date_modified":"2013-12-16T22:26:31Z","id":"https://caiustheory.com/add-to-icloud-reading-list-programmatically/","url":"https://caiustheory.com/add-to-icloud-reading-list-programmatically/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eOne piece of a larger puzzle I\u0026rsquo;m trying to solve currently, was how to add a given URL to my Apple \u0026ldquo;\u003ca href=\"http://www.apple.com/uk/safari/#icloud\"\u003eReading List\u003c/a\u003e\u0026rdquo; that is stored in iCloud and synced across all my OS X and iOS devices. More specifically, I wanted to add URLs to the list from my mac running Mavericks (10.9). I had a quick look at the Cocoa APIs and couldn\u0026rsquo;t see anything in OS X to do this. (iOS has an API to do it from Cocoa-land it seems though.)\u003c/p\u003e\n\u003cp\u003eI figured \u003ca href=\"http://www.apple.com/uk/safari/\"\u003eSafari.app\u003c/a\u003e was the key to getting this done on OS X, given it has the ability itself to add the current page to the reading list, either via a keyboard command, a menu item, or a button in the address bar. One quick mental leap later, and I was wondering if the engineers at Apple had been nice enough to expose that via Applescript for me to take advantage of.\u003c/p\u003e\n\u003cp\u003eOne quick stop in \u0026ldquo;Script Editor.app\u0026rdquo; later, and I had the Applescript dictionary open for Safari.app. Lo and behold, there is rather handily an Applescript command called \u0026ldquo;add reading list item\u0026rdquo;, which does \u003cstrong\u003eexactly\u003c/strong\u003e what I want. It has a few different options you can call it with, depending on whether you want Safari to go populate the title \u0026amp; preview text, or if you want to specify it yourself at save-time.\u003c/p\u003e\n\u003cp\u003eAs I want to be able to call this from multiple runtimes, I\u0026rsquo;ve chosen to save it as an executable, which leans on \u003ca href=\"https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/osascript.1.html\"\u003e\u003ccode\u003eosascript\u003c/code\u003e\u003c/a\u003e to run the actual Applescript. And here it is:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-applescript\" data-lang=\"applescript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e#!/usr/bin/env osascript\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eon\u003c/span\u003e \u003cspan class=\"nb\"\u003erun\u003c/span\u003e \u003cspan class=\"nv\"\u003eargv\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003ecount\u003c/span\u003e \u003cspan class=\"k\"\u003eof\u003c/span\u003e \u003cspan class=\"nv\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003etell\u003c/span\u003e \u003cspan class=\"nv\"\u003eapp\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Safari\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eto\u003c/span\u003e \u003cspan class=\"nv\"\u003eadd\u003c/span\u003e \u003cspan class=\"nv\"\u003ereading\u003c/span\u003e \u003cspan class=\"nv\"\u003elist\u003c/span\u003e \u003cspan class=\"nb\"\u003eitem\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eitem\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"k\"\u003eof\u003c/span\u003e \u003cspan class=\"nv\"\u003eargv\u003c/span\u003e \u003cspan class=\"k\"\u003eas \u003c/span\u003e\u003cspan class=\"nc\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eend\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e \u003cspan class=\"nb\"\u003erun\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSave it as whatever you want (eg. \u003ccode\u003eadd_to_reading_list\u003c/code\u003e), make it executable (\u003ccode\u003echmod +x add_to_reading_list\u003c/code\u003e), and then run it with the URL you want saving as the first argument.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ add_to_reading_list \u0026quot;http://caius.name/\u0026quot;\n$ add_to_reading_list \u0026quot;http://google.com/\u0026quot;\n# … etc …\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cem\u003e(Adding support for specifying preview text and title is left as an exercise for the reader!)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eHave fun reading later!\u003c/p\u003e\n"},{"title":"North West Triathlon 2013","date_published":"2013-09-12T10:00:00Z","date_modified":"2013-09-12T10:00:00Z","id":"https://caiustheory.com/north-west-triathlon-2013/","url":"https://caiustheory.com/north-west-triathlon-2013/","content_html":"\u003cp\u003e\u0026ldquo;Fun\u0026rdquo; triathlon (super sprint distance) done in Nantwich. First ever Triathlon, 7 months after starting training properly.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e200m swim: 00:04:08\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eDespite feeling like I was going super slow. Brine pool was fairly nice.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e20km bike: 00:52:31\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003ePretty happy with that, strongest discipline for me anyway.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2.5km run: 00:21:08\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWalked 2/3s of it at a guess. Really hate running, so happyish with this. Need to work on running over the winter.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTotal: 1h 17m 47s\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBOOYA. Enjoyable. Pretty damn happy with that (had a vague plan of being under 1h30m. So yay.)\u003c/p\u003e\n"},{"title":"Happy Villanelle","date_published":"2013-02-22T15:15:03Z","date_modified":"2013-02-22T15:15:03Z","id":"https://caiustheory.com/happy-villanelle/","url":"https://caiustheory.com/happy-villanelle/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eThe writing group that Carole and Liberty belong to had a homework this month to write a Villanelle. It\u0026rsquo;s a very specific kind of poem, written to both a repeating line structure and a rhyming pattern. (If you go read \u003ca href=\"http://libertyfallsdown.wordpress.com/2013/02/22/villanelle/\"\u003eLiberty\u0026rsquo;s villanelle\u003c/a\u003e she explains it properly at the start.)\u003c/p\u003e\n\u003cp\u003eHowever, after reading both \u003ca href=\"http://carolefindsherwings.wordpress.com/2013/02/23/ward-26-a-villanelle/\"\u003eCarole\u0026rsquo;s\u003c/a\u003e and \u003ca href=\"http://libertyfallsdown.wordpress.com/2013/02/22/villanelle/\"\u003eLiberty\u0026rsquo;s\u003c/a\u003e, I was feeling they\u0026rsquo;d both done very depressing poems. It might be a strict structure of poem, but there\u0026rsquo;s no need to make the subject matter so down!\u003c/p\u003e\n\u003cp\u003eSo here\u0026rsquo;s my overly enthusiastically attempt to try and balance the world of villanelle\u0026rsquo;s into something less depressing!\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eStriding out down the street at last,\u003cbr\u003e\nLike I own this town,\u003cbr\u003e\nTipping my hat to all that I passed.\u003c/p\u003e\n\u003cp\u003eSmiling at a friendly chugger who asked,\u003cbr\u003e\nGrinning at an exuberant clown,\u003cbr\u003e\nStriding out down the street at last.\u003c/p\u003e\n\u003cp\u003eSaluting the bookie behind his glass,\u003cbr\u003e\nTurning frowns upside down,\u003cbr\u003e\nTipping my hat to all that I passed.\u003c/p\u003e\n\u003cp\u003eTapping my feet and having a blast,\u003cbr\u003e\nAbusing verbs like they were a noun,\u003cbr\u003e\nStriding out down the street at last.\u003c/p\u003e\n\u003cp\u003eBeing amused by kids playing on grass,\u003cbr\u003e\nSeeing the queen in her glorious crown,\u003cbr\u003e\nTipping my hat to all that I passed.\u003c/p\u003e\n\u003cp\u003eCompared to sadness I am a contrast,\u003cbr\u003e\nCheering folk up in my dressing gown,\u003cbr\u003e\nStriding out down the street at last,\u003cbr\u003e\nTipping my hat to all that I passed.\u003c/p\u003e\n\u003c/blockquote\u003e\n"},{"title":"Pair new device with Nexxus Drive Transmit Pro","date_published":"2013-02-12T18:26:59Z","date_modified":"2013-02-12T18:26:59Z","id":"https://caiustheory.com/pair-new-device-with-nexxus-drive-transmit-pro/","url":"https://caiustheory.com/pair-new-device-with-nexxus-drive-transmit-pro/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eThe device is a bluetooth to FM transmitter with Model number NEX-FMTX-BTCK. (Hereafter DTP.)\u003c/p\u003e\n\u003cp\u003eThe set of instructions I use to pair it to a new device, having lost the user manual/instruction booklet, is as follows:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eRemove all previously paired devices from range (turn them off/move them away from the DTP.)\u003c/li\u003e\n\u003cli\u003eTurn the power on to the DTP without touching any buttons.\u003c/li\u003e\n\u003cli\u003eLet it flash the blue call button a couple of times as it searches for known devices.\u003c/li\u003e\n\u003cli\u003eHold the call button down until the call/hangup buttons flash blue/red respectively.\u003c/li\u003e\n\u003cli\u003eFind the \u0026ldquo;Drive Transmit Pro\u0026rdquo; on your device you want paired to it and pair with DTP.\u003c/li\u003e\n\u003cli\u003eTest out the pairing by calling your girlfriend and talking to her in a funny accent.\u003c/li\u003e\n\u003cli\u003eCelebrate with a beer!\u003c/li\u003e\n\u003c/ol\u003e\n"},{"title":"Church intertwined with State","date_published":"2013-02-05T23:06:18Z","date_modified":"2013-02-05T23:06:18Z","id":"https://caiustheory.com/church-intertwined-with-state/","url":"https://caiustheory.com/church-intertwined-with-state/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eIn the UK currently we\u0026rsquo;re moving (slowly) toward the \u003ca href=\"http://www.publications.parliament.uk/pa/bills/cbill/2012-2013/0126/cbill_2012-20130126_en_1.htm\"\u003esame sex marriage bill\u003c/a\u003e making it\u0026rsquo;s way into law. (It\u0026rsquo;s got a way to go still, but support seems to be there now at least.)\u003c/p\u003e\n\u003cp\u003eFor the record, I\u0026rsquo;m glad to see the state moving toward allowing two people to be united under law, irrespective of terminology used - and with equal rights to those currently enjoyed by the uniting of a man \u0026amp; woman to date. But for a while now I\u0026rsquo;ve been thinking that the discussion is presented in an unclear fashion, which causes people to jump to polarising positions on it, without any common ground.\u003c/p\u003e\n\u003cp\u003eThe way I see it is the UK has been a country with Religion and State intertwined for many, many hundreds of years - and therefore we have some constructs that exist both in law, and religion, but are conflated under one word and changing one appears to be changing them both.\u003c/p\u003e\n\u003cp\u003eThe Church of England is Protestant, because Henry VIII wanted to get a divorce and the Catholics wouldn\u0026rsquo;t allow him that, so we as a country \u0026ldquo;forked\u0026rdquo; our own religion that wasn\u0026rsquo;t so different, but allowed divorce of marriage. Times were slightly different back then, the church had a lot more direct power in terms of laws \u0026amp; how people lived. We\u0026rsquo;re a more diverse society today, and the church has less (direct?) involvement in our laws, although older laws are still based upon that original conflation of state and church.\u003c/p\u003e\n\u003cp\u003eI almost wonder if in this case (and others) we need to start diverging church from state and allowing the state to change things irrespective of the church, whilst still allowing the church to continue unhindered with practices it\u0026rsquo;s held for many years. Seems like the best of both worlds almost - in law we are a very generic and equal society, and if you wish to belong to or align with a group that has more specific beliefs, then that is open to you as well.\u003c/p\u003e\n\u003cp\u003eLets jump back to gay marriage here as an example then. If we define state marriage to be \u0026ldquo;the unity of two human beings, irrespective of their attributes\u0026rdquo;, then everyone is equal to join with a partner in the eyes of the law, and all such partnerships are afforded the lawful benefits that come with that unity. The church (and indeed, all religions) are also then free to define religious marriage in their doctrine as they wish, so they can say that for them it\u0026rsquo;s only permissible between a man and a woman. And if you wish to join with a partner following that doctrine, then you have to meet their rules.\u003c/p\u003e\n\u003cp\u003eIn this circumstance, I wonder how much of it is a hangup on the word \u0026ldquo;marriage\u0026rdquo;. I can see how the state wants to keep hold of it to describe partnerships between two people, and also that religions want to keep hold of it as they\u0026rsquo;ve been using it for years and neither wish to yield the word for the other to use. Which possibly means people think solely of this issue as just being an intertwined construct between state and religion, and not as the state giving everyone equality being a separate issue.\u003c/p\u003e\n"},{"title":"evil.rb","date_published":"2013-01-23T11:07:43Z","date_modified":"2013-01-23T11:07:43Z","id":"https://caiustheory.com/evil-rb/","url":"https://caiustheory.com/evil-rb/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eHere be hax. Don\u0026rsquo;t \u003cstrong\u003eever\u003c/strong\u003e do these. ;-)\u003c/p\u003e\n\u003ch2 id=\"reduce-local-variables-with-instance_eval\"\u003eReduce local variables with instance_eval\u003c/h2\u003e\n\u003cp\u003eSometimes (usually in a one-liner) I want to do some work with a value without assigning it to a variable. Chucking an \u003ccode\u003e#instance_eval\u003c/code\u003e call in there will set \u003ccode\u003eself\u003c/code\u003e to the value, which saves having to assign it to a local value. Pretty much only used by me in one-off scripts or cli commands.\u003c/p\u003e\n\u003ch4 id=\"good\"\u003eGood\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003estart_date\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eend_date\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;24 Dec 2011\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;23 Jan 2013\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ed\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"no\"\u003eDate\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ed\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003estart_date\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e to \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003eend_date\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e is \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eend_date\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"n\"\u003estart_date\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_i\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e days\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"bad\"\u003eBad\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;24 Dec 2011\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;23 Jan 2013\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ed\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"no\"\u003eDate\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ed\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einstance_eval\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e to \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003elast\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e is \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elast\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"n\"\u003efirst\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_i\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e days\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSee, way less code! \u003cem\u003ecough, cough\u003c/em\u003e\u003c/p\u003e\n\u003ch3 id=\"bonus-usage-misdirection\"\u003eBonus usage: Misdirection\u003c/h3\u003e\n\u003cp\u003eI also dropped some instance_eval on our campfire bot at \u003ca href=\"https://emberads.com/\"\u003eEmberAds\u003c/a\u003e to always blame one person, but without the code reading as such.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%w{Dom Mel Caius CBetta Baz}\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esample\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einstance_eval\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"s2\"\u003e\u0026#34;(4V5A8F5T=\u0026amp;$`\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eunpack\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;u\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat does not return one of the array elements as you might think it does from quickly scanning the code…\u003c/p\u003e\n\u003ch2 id=\"set-method-local-variables-in-default-arguments\"\u003eSet method-local variables in default arguments\u003c/h2\u003e\n\u003cp\u003eYou have a method and it takes one argument, which has a default value of \u003ccode\u003enil\u003c/code\u003e specified. You then run into the situation where you need to know if \u003ccode\u003enil\u003c/code\u003e was passed to the method, or if you\u0026rsquo;re getting the default value of \u003ccode\u003enil\u003c/code\u003e. You could change the default value to something you choose to be the \u0026ldquo;default value\u0026rdquo; and unlikely to be passed from elsewhere as the argument\u0026rsquo;s value, and reset the parameter to \u003ccode\u003enil\u003c/code\u003e after checking it, like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eoutput\u003c/span\u003e \u003cspan class=\"nb\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"ss\"\u003e:default_value\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"ss\"\u003e:default_value\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;caius\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edefault\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"s2\"\u003e\u0026#34;name: \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"nb\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einspect\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e -- default: \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003edefault\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einspect\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eoutput\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;name: \\\u0026#34;caius\\\u0026#34; -- default: true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eoutput\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;fred\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;name: \\\u0026#34;fred\\\u0026#34; -- default: nil\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat\u0026rsquo;s quite a lot of code added to the method just to find out if we passed a default value or not. And if we forget to reset the value when it\u0026rsquo;s \u003ccode\u003e:default_value\u003c/code\u003e then we end up leaking that into whatever the method does with that value. We also have the problem that one day the program \u003cem\u003ecould\u003c/em\u003e possibly send that \u0026ldquo;default value\u0026rdquo; we\u0026rsquo;ve chosen as the actual parameter, and we\u0026rsquo;d blindly change it thinking it was set as the default value, not the passed argument.\u003c/p\u003e\n\u003cp\u003eInstead we could (ab)use the power of ruby, and have ruby decide to set \u003ccode\u003edefault = true\u003c/code\u003e for us when, and only when, the variable is set \u003cem\u003eto\u003c/em\u003e the default value.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eoutput\u003c/span\u003e \u003cspan class=\"nb\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"n\"\u003edefault\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;caius\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"s2\"\u003e\u0026#34;name: \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"nb\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einspect\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e -- default: \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003edefault\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einspect\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eoutput\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;name: \\\u0026#34;caius\\\u0026#34; -- default: true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eoutput\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;fred\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;name: \\\u0026#34;fred\\\u0026#34; -- default: nil\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAs you can see, the output is identical. Yet we have no extra code \u003cem\u003einside\u003c/em\u003e the method to figure out if we were given the default value or not. And as a bonus to that, we no longer have to check for a specific value being passed and presume that is actually the default, and not one passed by the program elsewhere.\u003c/p\u003e\n\u003cp\u003eI posted this one in \u003ca href=\"https://gist.github.com/1528785\"\u003ea gist\u003c/a\u003e a while back (to show \u003ca href=\"http://avdi.org/\"\u003eAvdi\u003c/a\u003e it looks like), and people came up with some more insane things to do with it, including \u003ca href=\"https://gist.github.com/1528785#comment-71861\"\u003ereturning early\u003c/a\u003e, \u003ca href=\"https://gist.github.com/1528785#comment-71862\"\u003eraising errors\u003c/a\u003e or even \u003ca href=\"https://gist.github.com/1528785#comment-71876\"\u003eredefining the current method\u003c/a\u003e, all from the argument list! I\u0026rsquo;d suggest going to read them, it\u0026rsquo;s a mixture of OMG HAHA and OMFG NO WAY WHYY?!?!.\u003c/p\u003e\n\u003ch3 id=\"dont-do-this\"\u003eDon\u0026rsquo;t do this.\u003c/h3\u003e\n\u003cp\u003eDon\u0026rsquo;t do the above. No really, don\u0026rsquo;t do them. Unless you\u0026rsquo;re writing a one-off thing. But seriously, don\u0026rsquo;t do them. :-D\u003c/p\u003e\n"},{"title":"Some Small Refactorings in Ruby","date_published":"2013-01-16T12:23:54Z","date_modified":"2013-01-16T12:23:54Z","id":"https://caiustheory.com/some-small-refactorings-in-ruby/","url":"https://caiustheory.com/some-small-refactorings-in-ruby/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eHere\u0026rsquo;s a few things I refactor as I write code down initially. Not entirely convinced it\u0026rsquo;s strictly refactoring, but it\u0026rsquo;s how I amend from one pattern I see in a line or three of code into a different structure that I feel achieves the same result with cleaner or more concise code.\u003c/p\u003e\n\u003ch3 id=\"multiple-equality-comparisons\"\u003eMultiple equality comparisons\u003c/h3\u003e\n\u003cp\u003eTesting the equality of an object against another is fairly simple, just do \u003ccode\u003efoo == \u0026quot;bar\u0026quot;\u003c/code\u003e. However, I usually try to test against multiple objects in a slightly different way. Your first thought might be that the easiest way is just to chain a series of \u003ccode\u003e==\u003c/code\u003e with the OR (\u003ccode\u003e||\u003c/code\u003e) operator.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bar\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"n\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;baz\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"n\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"ss\"\u003e:sed\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"n\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI much prefer to flip it around, think of the objects I\u0026rsquo;m testing against as a collection (\u003ccode\u003eArray\u003c/code\u003e), and then ask them if they contain the object I\u0026rsquo;m checking. And for that, I use \u003ccode\u003eArray#include?\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;bar\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;baz\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:sed\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003einclude?\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efoo\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003e(And if you\u0026rsquo;re only testing against strings, you could use \u003ccode\u003e%w(bar baz)\u003c/code\u003e as a shortcut to create the array. Here\u0026rsquo;s more \u003ca href=\"http://caiustheory.com/ruby-shortcuts\"\u003eruby shortcuts\u003c/a\u003e.)\u003c/em\u003e\u003c/p\u003e\n\u003ch3 id=\"assigning-multiple-items-from-a-nested-hash-to-variables\"\u003eAssigning multiple items from a nested hash to variables\u003c/h3\u003e\n\u003cp\u003eOccasionally I find myself needing to be given a hash of a hash of data (most recently, an \u003ca href=\"https://github.com/intridea/omniauth/wiki\"\u003eomniauth\u003c/a\u003e auth hash) and assign some values from it to separate variables within my code. Given the following hash, containing a nested hash:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003edetails\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"ss\"\u003euid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;12345\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"ss\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Caius Durling\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"ss\"\u003enickname\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;caius\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLets say we want to extract the name and nickname fields from \u003ccode\u003edetails[:info]\u003c/code\u003e hash into their own local variables (or instance variables within a class, more likely.) We should probably handle the case of \u003ccode\u003edetails[:info]\u003c/code\u003e not being a hash, and try not to read from it if that\u0026rsquo;s the case - so we might end up with something like the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edetails\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:info\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003edetails\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:info\u003c/span\u003e\u003cspan class=\"o\"\u003e][\u003c/span\u003e\u003cspan class=\"ss\"\u003e:name\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003enickname\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edetails\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:info\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003edetails\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:info\u003c/span\u003e\u003cspan class=\"o\"\u003e][\u003c/span\u003e\u003cspan class=\"ss\"\u003e:nickname\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ename\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;Caius Durling\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003enickname\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;caius\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd then in the spirit of \u003ca href=\"http://en.wikipedia.org/wiki/Don't_repeat_yourself\"\u003eDRYing\u003c/a\u003e up our code, we see there\u0026rsquo;s duplication in both lines in checking \u003ccode\u003edetails[:info]\u003c/code\u003e exists (not actually that it\u0026rsquo;s a hash, but hey ho, we rely on upstream to send us \u003ccode\u003enil\u003c/code\u003e or a hash.) So we reduce it down using an if statement and give ourselves slightly less to type at the same time.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e((\u003c/span\u003e \u003cspan class=\"n\"\u003einfo\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edetails\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:info\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:name\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003enickname\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:nickname\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ename\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;Caius Durling\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003enickname\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;caius\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"returning-two-values-conditionally\"\u003eReturning two values conditionally\u003c/h3\u003e\n\u003cp\u003eSometimes a method will end with a ternary, where depending on a condition it\u0026rsquo;ll either return one or another value. If this conditional returns true, then the first value is returned. Otherwise it returns the second value. You could quite easily write it out as an if/else longer-form block too.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003emy_method\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@blah\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003efoo\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"ss\"\u003e:foo_matches\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"ss\"\u003e:no_match\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMy brain finds picking the logic in this apart slightly harder mentally, than if I drop a \u003ca href=\"http://en.wikipedia.org/wiki/Return_early\"\u003ereturn early\u003c/a\u003e bomb on the method. Then it reads more akin to how I\u0026rsquo;d think through the logic. Return the first value if this conditional returns true. Otherwise the method returns this second value. I think the second value being on a completely separate line helps me make this mental distinction quicker too.\u003c/p\u003e\n\u003cp\u003eSo I\u0026rsquo;d write it this way:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003emy_method\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"ss\"\u003e:foo_matches\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"vi\"\u003e@blah\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003efoo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"ss\"\u003e:no_match\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"returning-nil-or-a-value-conditionally\"\u003eReturning nil or a value conditionally\u003c/h3\u003e\n\u003cp\u003eFollowing on from the last snippet, but taking advantage of the ruby runtime a bit more, is when you\u0026rsquo;re wanting to return a value if a conditional is true, or otherwise false. The easy way is to just write \u003ccode\u003enil\u003c/code\u003e in the ternary:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003emy_method\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@foo\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"ss\"\u003e:bar\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"ss\"\u003e:foo_matches\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kp\"\u003enil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHowever, we know ruby returns the result of the last expression in the method. And that if a single line conditional isn\u0026rsquo;t met, it returns nil from the expression. Combining that, we can rewrite the previous example into this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003emy_method\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"ss\"\u003e:foo_matches\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"vi\"\u003e@foo\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"ss\"\u003e:bar\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd it will still return nil in the case that \u003ccode\u003e@foo\u003c/code\u003e doesn\u0026rsquo;t match \u003ccode\u003e:bar\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"returning-a-boolean\"\u003eReturning a boolean\u003c/h3\u003e\n\u003cp\u003eSometimes you have a method that returns the result of a conditional, but it\u0026rsquo;s written to return true/false in a conditional instead.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003emy_method\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@foo\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"ss\"\u003e:bar\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe really easy refactor here is to just remove the ternary and leave the conditional.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003emy_method\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@foo\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"ss\"\u003e:bar\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd of course if you were returning \u003ccode\u003efalse\u003c/code\u003e when the conditional evaluates to \u003ccode\u003etrue\u003c/code\u003e, you can either negate the comparison (use \u003ccode\u003e!=\u003c/code\u003e in that example), or negate the entire conditional result by prepending \u003ccode\u003e!\u003c/code\u003e to the line.\u003c/p\u003e\n"},{"title":"Why I love DATA","date_published":"2013-01-08T19:09:42Z","date_modified":"2013-01-08T19:09:42Z","id":"https://caiustheory.com/why-i-love-data/","url":"https://caiustheory.com/why-i-love-data/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eIn a ruby script, there\u0026rsquo;s a keyword \u003ccode\u003e__END__\u003c/code\u003e that for a long time I thought just marked anything after it as a comment. So I used to use it to store snippets and notes about the script that weren\u0026rsquo;t really needed inline. Then one day I stumbled across the \u003ccode\u003eDATA\u003c/code\u003e constant, and wondered what flaming magic it was.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eDATA\u003c/code\u003e is in fact an IO object, that you can read from (or anything else you\u0026rsquo;d do with an IO object). It contains all the content after \u003ccode\u003e__END__\u003c/code\u003e in that ruby script file\u003c!-- raw HTML omitted --\u003e*\u003c!-- raw HTML omitted --\u003e. (It only exists when the file contains \u003ccode\u003e__END__\u003c/code\u003e, and for the first file ruby invokes though. See \u003c!-- raw HTML omitted --\u003efootnote\u003c!-- raw HTML omitted --\u003e for more details.)\u003c/p\u003e\n\u003cp\u003eHow can we use this, and why indeed do I love this fickle constant? I mostly use it for quick scripts where I need to process text data, rather than piping to STDIN.\u003c/p\u003e\n\u003cp\u003eGiven a list of URLs that I want to open in my web browser and look at, I could do the following for instance:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eDATA\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach_line\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"ss\"\u003e:chomp\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"sb\"\u003e`open \u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"sb\"\u003e\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e__END__\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003ehttp://google.com/\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003ehttp://yahoo.com/\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ewhich upon running (on a mac) would open all the URLs listed in DATA in my default web browser. (For bonus points, use \u003ca href=\"https://github.com/copiousfreetime/launchy#readme\"\u003eLaunchy\u003c/a\u003e for cross-platform compatibility.) Really handy \u0026amp; quick/simple when you\u0026rsquo;ve got 500+ URLs to open at once to go through. (I once had a job that required me to do this daily. Fun.)\u003c/p\u003e\n\u003cp\u003eOr given a bunch of CSV data that you just want one column for, you could reach for \u003ccode\u003ecut\u003c/code\u003e or \u003ccode\u003eawk\u003c/code\u003e in the terminal, but ruby has a really good CSV library which I trust and know how to use already. Why not just use that \u0026amp; \u003ccode\u003eDATA\u003c/code\u003e to pull out the field you want?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;csv\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eCSV\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"no\"\u003eDATA\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003eheaders\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003erow\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"n\"\u003erow\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;kName\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e__END__\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003ekId,kName,kURL\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e1,Google UK,http://google.co.uk\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e2,\u0026#34;Yahoo, UK\u0026#34;,http://yahoo.co.uk\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; Google UK\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; Yahoo, UK\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI find when the data I want to munge is already in my clipboard, and I can run ruby scripts directly from text editors without having to save a file, it saves having to write the data out to a file, have ruby read it back in, etc just to do something with the data. I can just write the script reading from \u003ccode\u003eDATA\u003c/code\u003e, paste the data in and run it. Which also lets me run it iteratively and build up a slightly more complex script that I don\u0026rsquo;t want to keep. Then do what I need with the output and close the file without saving it.\u003c/p\u003e\n\u003cp\u003e\u003c!-- raw HTML omitted --\u003e*\u003c!-- raw HTML omitted --\u003e technically DATA is an IO handler to read \u003ccode\u003e__FILE__\u003c/code\u003e, which has been wound forward to the start of the first line after \u003ccode\u003e__END__\u003c/code\u003e in the file. And it only exists for the first ruby file to be invoked by the interpreter.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecat \u0026gt; tmp/data.rb \u003cspan class=\"s\"\u003e\u0026lt;\u0026lt;RUBY\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003ep DATA.read\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e__END__\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003edata.rb\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003eRUBY\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eruby tmp/data.rb\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;data.rb\\n\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecat \u0026gt; tmp/data-require.rb \u003cspan class=\"s\"\u003e\u0026lt;\u0026lt;RUBY\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003erequire \u0026#34;./tmp/data\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003eRUBY\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eruby tmp/data-require.rb\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; /Users/caius/tmp/data.rb:1:in `\u0026lt;top (required)\u0026gt;\u0026#39;: uninitialized constant DATA (NameError)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd because it\u0026rsquo;s a file handle pointing at the current file, you can rewind it and read the entire ruby script into itself…\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ ruby tmp/readself.rb \nDATA.rewind\nprint DATA.read\n\n__END__\nsomething goes here\n\u003c/code\u003e\u003c/pre\u003e\n"},{"title":"Geolocation in nginx","date_published":"2012-12-04T09:42:00Z","date_modified":"2012-12-04T09:42:00Z","id":"https://caiustheory.com/geolocation-in-nginx/","url":"https://caiustheory.com/geolocation-in-nginx/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSometimes you need to have a rough idea of where your website visitor is located. There\u0026rsquo;s many ways to geolocate them, but if you just want to go to country level then \u003ca href=\"http://dev.maxmind.com/geoip/geolite\"\u003eMaxMind have free geo databases\u003c/a\u003e available to help you. When we needed to do this quickly on-the-fly at EmberAds, we came up with the \u003ca href=\"https://github.com/emberads/trifle#readme\"\u003etrifle\u003c/a\u003e gem, which supports ipv4 and ipv6 lookups.\u003c/p\u003e\n\u003cp\u003eRecently I was searching for something else to do with nginx and ran across \u003ca href=\"http://www.ruby-forum.com/topic/125810\"\u003ea mailing list thread\u003c/a\u003e about using the maxmind database with nginx\u0026rsquo;s \u003ca href=\"http://wiki.nginx.org/NginxHttpGeoModule\"\u003eHTTP Geo module\u003c/a\u003e and do the lookup directly in nginx itself. Finally got a chance to sit down and work out the logistics of doing this. I\u0026rsquo;ve done this on an ubuntu 12.04 box, with the expected config file layouts that come with ubuntu.\u003c/p\u003e\n\u003cp\u003eRun the following on your server (as someone with write access to nginx config files):\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Generate the text file for nginx to import\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eperl \u0026lt;\u003cspan class=\"o\"\u003e(\u003c/span\u003ecurl -s https://raw.github.com/nginx/nginx/master/contrib/geo2nginx.pl\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e\u0026lt; \u0026lt;\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003ezip\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003etempfile\u003cspan class=\"k\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003ecurl -so \u003cspan class=\"nv\"\u003e$zip\u003c/span\u003e http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e unzip -p \u003cspan class=\"nv\"\u003e$zip\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u0026gt; /etc/nginx/nginx_ip_country.txt\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Tell nginx to work out the IP country and store in variable\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;geo $IP_COUNTRY {\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e  default --;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e  include /etc/nginx/nginx_ip_country.txt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s1\"\u003e}\u0026#39;\u003c/span\u003e \u0026gt; /etc/nginx/conf.d/ip_country.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow go find the http block for the vhost you want to have the header passed to, and assuming it\u0026rsquo;s passenger, add the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003epassenger_set_cgi_param\u003c/span\u003e \u003cspan class=\"s\"\u003eHTTP_X_IP_COUNTRY\u003c/span\u003e \u003cspan class=\"nv\"\u003e$IP_COUNTRY\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e(If you don\u0026rsquo;t use passenger, look at the docs for \u003ca href=\"http://wiki.nginx.org/HttpProxyModule#proxy_pass_header\"\u003eproxy_pass_header\u003c/a\u003e or \u003ca href=\"http://wiki.nginx.org/HttpFastcgiModule#fastcgi_pass_header\"\u003efastcgi_pass_header\u003c/a\u003e to see which you\u0026rsquo;ll require for your setup.)\u003c/p\u003e\n\u003cp\u003eReload nginx, and behold, \u003ccode\u003erequest.env[\u0026quot;HTTP_X_IP_COUNTRY\u0026quot;]\u003c/code\u003e (assuming a rack app running under ruby) will be a two letter country code, or \u003ccode\u003e\u0026quot;--\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eUnfortunately this is IPv4 only currently, there\u0026rsquo;s a \u003ca href=\"http://forum.nginx.org/read.php?29,232648\"\u003ethread on the nginx mailing list from November 2012\u003c/a\u003e saying IPv6 support should be coming on the v1.3 branch of nginx, but with no known ETA. So currently for IPv6 support, take a look at \u003ca href=\"https://github.com/emberads/trifle#readme\"\u003eEmberAds\u0026rsquo; trifle gem\u003c/a\u003e instead.\u003c/p\u003e\n"},{"title":"Mounting Harman Kardon Soundsticks on the wall","date_published":"2012-11-06T20:46:45Z","date_modified":"2012-11-06T20:46:45Z","id":"https://caiustheory.com/mounting-harman-kardon-soundsticks-on-the-wall/","url":"https://caiustheory.com/mounting-harman-kardon-soundsticks-on-the-wall/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eHaving recently moved my Soundstick III\u0026rsquo;s into the front room, I\u0026rsquo;ve been thinking of a way to wall mount them safely to free up table room. Googling eventually turned up just one person who has previously documented \u003ca href=\"http://www.thanatopsic.org/hendrik/article/102/hot-hotdesking-action\"\u003emounting his soundsticks\u003c/a\u003e on the wall, using 22mm plumbing clips (intended for 22mm central heating pipes).\u003c/p\u003e\n\u003cp\u003eA quick scrounge round the local Homebase this afternoon yielded a pack of similar clips, 5x 22mm push clips for £1.99. Having just fitted the speakers to the wall, they\u0026rsquo;re nice and secure (providing no-one hangs on them, which they shouldn\u0026rsquo;t do), fairly neat and simple to fit.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve left the subwoofer on the floor under the table, and only mounted the \u0026ldquo;sticks\u0026rdquo; (tweeters) on the wall, one each side of the mirror over our dining table. I can then conveniently run the cables to the \u0026ldquo;sticks\u0026rdquo; behind the mirror and keep it looking neater.\u003c/p\u003e\n\u003cp\u003eI affixed the clips to the wall, one either side of the mirror with enough space for the speaker to sit without overlapping the mirror. Mostly just held the stick up and guessed at where to put the clip, but it looks ok.\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003cp\u003eThen I bent the speaker stand backwards behind the stick (don\u0026rsquo;t worry, it\u0026rsquo;s on a hinge!) and threaded the cable through the middle of the ring to make it sit flusher against the wall.\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003cp\u003eAnd then it was just a case of easing the speaker ring into the clip, with the clip at the top of the ring (picture below if you can\u0026rsquo;t visualise that easily). I think the rings are more like 24-25mm so it takes a bit of easing to get them in there. Once it\u0026rsquo;s in there\u0026rsquo;s no play in the clip for it to wiggle out though, even though it\u0026rsquo;s plastic. I tried not to fatigue the clip arms too much wiggling it in there as well, so as to minimise the chance of it failing over time. (Something to check periodically!)\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003cp\u003eLastly, I just zip tied the cables together behind the mirror, and ran them down vertically from the middle to the floor and plugged them in. Sounds fantastic, and due to being plugged into an \u003ca href=\"http://www.apple.com/airportexpress/\"\u003eAirport Express\u003c/a\u003e, anything compatible can stream audio through them wirelessly. Fabulous darling!\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003cp\u003e\u003cem\u003e(Click any image for a larger version)\u003c/em\u003e\u003c/p\u003e\n"},{"title":"Musical Quiz","date_published":"2012-10-10T14:11:41Z","date_modified":"2012-10-10T14:11:41Z","id":"https://caiustheory.com/musical-quiz/","url":"https://caiustheory.com/musical-quiz/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eUsing only song titles from ONE ARTIST, cleverly answer these questions. Try not to repeat a song title. It’s harder than you think.\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n"},{"title":"Human Affliction","date_published":"2012-04-09T14:04:32Z","date_modified":"2012-04-09T14:04:32Z","id":"https://caiustheory.com/human-affliction/","url":"https://caiustheory.com/human-affliction/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eThat damned human affliction,\u003cbr\u003e\nMissing someone like an addiction,\u003cbr\u003e\nThe cure\u0026rsquo;s quite simple,\u003cbr\u003e\nLike bursting a pimple,\u003cbr\u003e\nHug them, hold them, repetition.\u003c/p\u003e\n"},{"title":"Where did life go?","date_published":"2012-03-30T16:33:51Z","date_modified":"2012-03-30T16:33:51Z","id":"https://caiustheory.com/where-did-life-go/","url":"https://caiustheory.com/where-did-life-go/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eInto the past,\u003cbr\u003e\nwith our regrets and tears,\u003cbr\u003e\nand our successes and cheers.\u003c/p\u003e\n"},{"title":"Install capybara-webkit gem on Ubuntu","date_published":"2012-03-01T11:01:34Z","date_modified":"2012-03-01T11:01:34Z","id":"https://caiustheory.com/install-capybara-webkit-gem-on-ubuntu/","url":"https://caiustheory.com/install-capybara-webkit-gem-on-ubuntu/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eDear future Caius searching for this issue,\u003c/p\u003e\n\u003cp\u003eThe apt package you need to install to use the capybara-webkit rubygem on ubuntu (tested on 10.04 and 11.10) is \u003ccode\u003elibqt4-dev\u003c/code\u003e. That is, to \u003ccode\u003egem install capybara-webkit\u003c/code\u003e, you need to run \u003ccode\u003eaptitude install libqt4-dev\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eYours helpfully,\u003cbr\u003e\nPast Caius\u003c/p\u003e\n"},{"title":"Use Readline With Default Ruby on OS X","date_published":"2012-02-05T18:13:20Z","date_modified":"2012-02-05T18:13:20Z","id":"https://caiustheory.com/use-readline-with-default-ruby-on-os-x/","url":"https://caiustheory.com/use-readline-with-default-ruby-on-os-x/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eOS X Lion comes with ruby 1.8.7-p249 installed, although it\u0026rsquo;s compiled against libedit rather than libreadline. Whilst libedit is a mostly-compatible replacement for libreadline, I find there\u0026rsquo;s a couple of settings I\u0026rsquo;m used to that don\u0026rsquo;t work in libedit. (Like \u003ccode\u003ehistory-beginning-search-backward\u003c/code\u003e.)\u003c/p\u003e\n\u003cp\u003eLuckily you can grab the source of ruby and compile just the readline extension, and move it into the right place for it to just work. Here\u0026rsquo;s what\u0026rsquo;s been working for me:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Install readline using homebrew\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebrew install readline\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Download the ruby source and check out 1.8.7-p249\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir ~/tmp \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"nb\"\u003ecd\u003c/span\u003e ~/tmp\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit clone git://github.com/ruby/ruby\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e ruby\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit checkout v1_8_7_249\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e ext/readline\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eruby extconf.rb --with-readline-dir\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ebrew --prefix readline\u003cspan class=\"k\"\u003e)\u003c/span\u003e --disable-libedit\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emake\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow you should have \u003ccode\u003ereadline.bundle\u003c/code\u003e in the current directory, and it should be compiled against your homebrew-installed readline library, rather than libedit that comes with the system. We can quickly double-check that by using \u003ccode\u003eotool\u003c/code\u003e to check what the binary is linked against.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ otool -L readline.bundle\nreadline.bundle:\n    /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/libruby.1.dylib (compatibility version 1.8.0, current version 1.8.7)\n    /usr/local/Cellar/readline/6.2.2/lib/libreadline.6.2.dylib (compatibility version 6.0.0, current version 6.2.0)\n    /usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)\n    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd in the output you should see a line listing \u0026ldquo;libreadline\u0026rdquo;, and no lines listing \u0026ldquo;libedit\u0026rdquo;. Which that shows, we\u0026rsquo;ve compiled it properly then. Now the bundle is built we need to move it into the right place so it\u0026rsquo;s loaded when ruby is invoked.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eRL_PATH\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin11.0\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Back up the original bundle, just in cases\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo mv \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$RL_PATH\u003c/span\u003e\u003cspan class=\"s2\"\u003e/readline.bundle\u0026#34;\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$RL_PATH\u003c/span\u003e\u003cspan class=\"s2\"\u003e/readline.bundle.libedit\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo mv readline.bundle \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$RL_PATH\u003c/span\u003e\u003cspan class=\"s2\"\u003e/readline.bundle\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd that\u0026rsquo;s it. You\u0026rsquo;ve got a proper compiled-against-readline installed ruby 1.8.7-p249 on 10.7 now.\u003c/p\u003e\n\u003cp\u003eOne gotcha I ran into was needing to pass the same arguments to rvm when installing any other version of 1.8.7 on the same machine. Simple enough, just need to remember to do it though.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eCC\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003egcc-4.2 rvm install 1.8.7-p357 -C --with-readline-dir\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ebrew --prefix readline\u003cspan class=\"k\"\u003e)\u003c/span\u003e --disable-libedit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"End Of An Era","date_published":"2012-01-12T15:30:00Z","date_modified":"2012-01-12T15:30:00Z","id":"https://caiustheory.com/end-of-an-era/","url":"https://caiustheory.com/end-of-an-era/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI remember watching \u003ca href=\"http://www.youtube.com/watch?v=UF8uR6Z6KLc\"\u003eSteve Jobs\u0026rsquo; commencement speech\u003c/a\u003e for the first time and being fairly touched by the three stories he told in it. The major one that resonated with me at the time, and has since made more sense to me, is the first story he tells about joining the dots later on when you\u0026rsquo;re looking backwards. To quote from it:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eOf course, it was impossible to connect the dots looking forward when I was in college. But it was very very clear looking backwards ten years later. Again, you can\u0026rsquo;t connect the dots looking forward, you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eIt made me think back to a time when I was a teenager and someone far older and wiser than me told me one way to think about the future and how what we do today affects where we end up. He described it as holding a piece of string in your hand, with the other end dangling free. The end of the string not attached to you is the future, and where you\u0026rsquo;re holding it is the present. As you do things in life your hand moves around, flicking the string around and amplifying the movements of the present in the future.\u003c/p\u003e\n\u003cp\u003eI think what he was trying to impart by telling me that story was to be careful not to do anything too extravagant in the present (like get expelled from school for instance), so as not to affect the future too much. Which I never really took on board at the time, but now I look back at the dots of my (some would say) rather short life so far, and connect them to see how some things influenced other things and how everything works out in the end. But that sometimes you need to push yourself or do something unexpected to get there.\u003c/p\u003e\n\u003ch3 id=\"looking-backwards-to-go-forward\"\u003eLooking Backwards to go Forward\u003c/h3\u003e\n\u003cp\u003eWhen I look back over the last few years of my life, I find the dots quite amazing to connect. First there was BarCamp Sheffield 2007 where I met \u003ca href=\"http://www.thehodge.co.uk/\"\u003eDom\u003c/a\u003e, and there was Twitter and BarCamp Manchester \u0026amp; Leeds where I met geeks like \u003ca href=\"http://www.3hv.co.uk/\"\u003eRahoul\u003c/a\u003e, \u003ca href=\"http://johnleach.co.uk/\"\u003eJohn\u003c/a\u003e and \u003ca href=\"http://brightbox.co.uk/\"\u003eJeremy\u003c/a\u003e both online and offline. And then in June 2008 I moved to Leeds, sharing a house with Dom and quite quickly ended up being hired into what was basically my dream first job at \u003ca href=\"http://www.brightbox.com/\"\u003eBrightbox\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eAnd that\u0026rsquo;s where I\u0026rsquo;ve been working for the last 1198 days, having far too much fun, solving weird, wonderful and sometimes downright frustrating problems, all with fantastically awesome and hilarious colleagues. And working on a massive variety of things, from \u003ca href=\"https://github.com/brightbox/rujitsu/\"\u003etiny utilities\u003c/a\u003e to the newly launched \u003ca href=\"http://www.brightbox.com/\"\u003eCloud Platform\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eAnd today I flick my wrist ever so slightly more than normal by leaving Brightbox, without really knowing where the future end of the string will eventually end up, but knowing that I\u0026rsquo;ll look back in a few years and see dots connected that I can\u0026rsquo;t imagine today. I truly don\u0026rsquo;t think I could have had a better job to start off as a professional geek, and especially want to thank \u003ca href=\"http://johnleach.co.uk/\"\u003eJohn\u003c/a\u003e and \u003ca href=\"http://brightbox.co.uk/\"\u003eJeremy\u003c/a\u003e for hiring me and helping me start my career in the best way possible.\u003c/p\u003e\n\u003cp\u003eAs for my next challenge, I\u0026rsquo;m solving interesting problems in the online advertising world with \u003ca href=\"http://cristianobetta.com/\"\u003eCristiano\u003c/a\u003e, \u003ca href=\"http://www.thehodge.co.uk/\"\u003eDom\u003c/a\u003e, \u003ca href=\"http://missgeeky.com/\"\u003eMelinda\u003c/a\u003e and \u003ca href=\"http://www.3hv.co.uk/\"\u003eRahoul\u003c/a\u003e. And we\u0026rsquo;re bloody amazing together. :-)\u003c/p\u003e\n"},{"title":"Nighttime Horrors","date_published":"2012-01-10T10:51:03Z","date_modified":"2012-01-10T10:51:03Z","id":"https://caiustheory.com/nighttime-horrors/","url":"https://caiustheory.com/nighttime-horrors/","author":{"name":"Caius Durling"},"content_html":"\u003cblockquote\u003e\n\u003cp\u003eScreaming ghouls and scary monsters,\u003cbr\u003e\nPermeating our subconscious,\u003cbr\u003e\nAt night they roam freely through our dreams,\u003cbr\u003e\nWith the sole intent of causing our screams,\u003cbr\u003e\nScaring us until we awake,\u003cbr\u003e\nAnnoyingly, for we know they are fake.\u003c/p\u003e\n\u003c/blockquote\u003e\n"},{"title":"Defining Ruby Superclasses On The Fly","date_published":"2011-12-18T01:13:14Z","date_modified":"2011-12-18T01:13:14Z","id":"https://caiustheory.com/defining-ruby-superclasses-on-the-fly/","url":"https://caiustheory.com/defining-ruby-superclasses-on-the-fly/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eAny rubyist that\u0026rsquo;s defined a class should understand the following class definition:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eFoo\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"no\"\u003eObject\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt creates a new Constant (\u003ccode\u003eFoo\u003c/code\u003e) that is a subclass of \u003ccode\u003eObject\u003c/code\u003e. Pretty straightforward. But what you might not know is that ruby executes each line it reads in as it reads them. So we could do the following to show that:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eFoo\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"no\"\u003eObject\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;we just defined object!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd get the following output when we run that file:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; we just defined object!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSo.. we know ruby is executing things on the fly whilst defining classes for us. How can we use this for profit and shenanigans?! (Err, use this for vanquishing evil and creating readable code I mean. Honest.)\u003c/p\u003e\n\u003cp\u003eHow about if we\u0026rsquo;ve got a couple of opinionated people who like to think they\u0026rsquo;ve got the biggest ego in the interpreter? The last one to be loaded likes to have any new people ushered into the interpreter to be a subclass of themselves. Lets abuse a global for storing it in, and use a method to choose that on the fly when creating a new class.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ecurrent_awkward_bugger\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vg\"\u003e$awkward_bugger\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eSimon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"vg\"\u003e$awkward_bugger\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eSimon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eFred\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrent_awkward_bugger\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eFred\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esuperclass\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; Simon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eHarold\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"vg\"\u003e$awkward_bugger\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eHarold\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eJohn\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrent_awkward_bugger\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eJohn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esuperclass\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; Harold\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eFred\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esuperclass\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; Simon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOkay, so that looks a bit different to normally defined classes. We create \u003ccode\u003eSimon\u003c/code\u003e, assign him to the awkward bugger global then create \u003ccode\u003eFred\u003c/code\u003e, who subclasses the return value of the \u003ccode\u003ecurrent_awkward_bugger\u003c/code\u003e method which happens to be \u003ccode\u003eSimon\u003c/code\u003e currently. Then \u003ccode\u003eHarold\u003c/code\u003e muscles his way into the interpreter and decides he\u0026rsquo;s going to be the current awkward bugger, so poor \u003ccode\u003eJohn\u003c/code\u003e gets to subclass \u003ccode\u003eHarold\u003c/code\u003e even though he\u0026rsquo;s defined in the same way as \u003ccode\u003eFred\u003c/code\u003e. (And as you can see on the last line, Fred\u0026rsquo;s superclass is unchanged even though we changed the \u003ccode\u003eawkward_bugger\u003c/code\u003e global.)\u003c/p\u003e\n\u003cp\u003eOn a somewhat related note there\u0026rsquo;s a lovely method called \u003ccode\u003econst_missing\u003c/code\u003e that gets invoked when you call a Constant in ruby that isn\u0026rsquo;t defined. (Much like \u003ccode\u003emethod_missing\u003c/code\u003e if you\u0026rsquo;re familiar with that.) Means you can do even more shenanigans with non-existent superclasses for classes you\u0026rsquo;re defining.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eSimon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eHarold\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eObject\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nc\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003econst_missing\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ekonstant\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"no\"\u003eSimon\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"no\"\u003eHarold\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003eshuffle\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eFred\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"no\"\u003eArrogantBastard\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eFred\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esuperclass\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; Simon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eAlbert\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"no\"\u003eArrogantBastard\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eAlbert\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esuperclass\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; Harold\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSo here we\u0026rsquo;re choosing from Simon and Harold on the fly each time a missing constant is referenced (in this case the aptly named \u003ccode\u003eArrogantBastard\u003c/code\u003e constant.) If you run this code yourself you\u0026rsquo;ll see the superclasses change on each run according to what your computer picks that time.\u003c/p\u003e\n"},{"title":"Magic Bubble","date_published":"2011-12-06T22:03:46Z","date_modified":"2011-12-06T22:03:46Z","id":"https://caiustheory.com/magic-bubble/","url":"https://caiustheory.com/magic-bubble/","author":{"name":"Caius Durling"},"content_html":"\u003cblockquote\u003e\n\u003cp\u003eYou and me,\u003cbr\u003e\nQuietly,\u003cbr\u003e\nWatching the rain in the stars,\u003cbr\u003e\nFrom a bubble near Mars.\u003c/p\u003e\n\u003c/blockquote\u003e\n"},{"title":"Experimental Procrastination","date_published":"2011-11-07T00:41:49Z","date_modified":"2011-11-07T00:41:49Z","id":"https://caiustheory.com/experimental-procrastination/","url":"https://caiustheory.com/experimental-procrastination/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eA while ago I read a blog post that Elizabeth N wrote, on the \u003ca href=\"http://naramore.net/blog/the-value-of-self-serving-code\"\u003evalue of writing self-serving code\u003c/a\u003e. Ever since I\u0026rsquo;ve been moderately aware of when I\u0026rsquo;ve written self-serving code, usually either at hackdays, or just little projects where I\u0026rsquo;m either experimenting with something or just bashing out a new idea.\u003c/p\u003e\n\u003cp\u003eIn fact, I even wrote about one of my recent \u0026ldquo;self-serving\u0026rdquo; projects I bashed out in an evening, \u003ca href=\"http://caiustheory.com/tweetsavr\"\u003eTweetSavr\u003c/a\u003e. It has no tests, was written moderately quickly and not refactored immensely (here\u0026rsquo;s hoping the git history backs me up on that! I certainly didn\u0026rsquo;t knowingly majorly refactor it at any point at least.)  But it \u0026ldquo;scratched an itch\u0026rdquo; and solved the problem I had, and it works for the limited use case I need it to, so it\u0026rsquo;s a success as far as I\u0026rsquo;m concerned.\u003c/p\u003e\n\u003cp\u003eMore recently a friend remarked to me in a private conversation that everyone needs to procrastinate occasionally, to save them going \u0026ldquo;stir crazy\u0026rdquo;. Whilst I agree with what she said, everyone needs to switch gears and do something that perhaps you shouldn\u0026rsquo;t, or that won\u0026rsquo;t directly contribute to completing your current task, I couldn\u0026rsquo;t help but draw a link between procrastinating and writing self-serving code.\u003c/p\u003e\n\u003cp\u003eNow I\u0026rsquo;m a programmer, it\u0026rsquo;s what I did for a hobby through school, it\u0026rsquo;s what I leapt into a career doing when I was offered the chance and even when I\u0026rsquo;ve had a particularly exhausting week, it\u0026rsquo;s something I\u0026rsquo;ll eventually turn back to. But I realised that often when I procrastinate, I do so by writing self-serving code. My creative output or process if you will is to create things digitally on the computer, be that a web app, hacky script to let me do something I\u0026rsquo;m not supposed to be able to with someone else\u0026rsquo;s application, just dicking around or exploring whatever tidbit of interesting info/behaviour in a language or library someone\u0026rsquo;s just shared on IRC.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eAside: I\u0026rsquo;ve often joked (semi-seriously) that if/when I have enough cash in the bank to not have to actually have a \u0026ldquo;day job\u0026rdquo;, I\u0026rsquo;ll just spend all day building the random ideas that get tossed out on IRC instead. Quite often the smaller things I code up anyway already of an evening and they end up in my \u003ca href=\"https://gist.github.com/caius\"\u003egists\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eNow it\u0026rsquo;s unhealthy and counter-productive to want to program 24/7, at least in the long term. (Doing the odd 24 hour hackday event here and there can mean winning fun prizes to play with however.) And sometimes all that you need to do to solve a problem you\u0026rsquo;ve been butting your head against for the last couple of hours is to get off the damn computer. (I usually find taking a shower makes my subconscious reveal the answer it\u0026rsquo;s been quietly computing and hey presto, I know how to solve the problem properly!) At other times it just requires changing gears and flexing a different part of your brain muscle. Like say, writing self-serving code. And procrastinating by doing so.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m not entirely sure what the point of this thought process is, or if there can really be a point to it, but it really intrigued me drawing a link between procrastinating and writing self-serving code. I can imagine other creatively minded people might do much the same thing, an artist just sketching for the sake of sketching, or a writer taking a couple of hours off from her next novel to write a short story for her blog. (That last one is actually something a friend\u0026rsquo;s done, go read her \u003ca href=\"http://libertyfallsdown.wordpress.com/tag/short-story/\"\u003eshort stories\u003c/a\u003e, they\u0026rsquo;re quite good. Start from the bottom though.)\u003c/p\u003e\n\u003cp\u003eAnd of course sometimes you just need to vegetate and read facebook (or twitter), but constructive procrastination does serve a real purpose I think, and can be quite useful as well.\u003c/p\u003e\n"},{"title":"Install GCC-4.2.1 (Apple build 5666.3) with Xcode 4.2","date_published":"2011-10-30T17:36:48Z","date_modified":"2011-10-30T17:36:48Z","id":"https://caiustheory.com/install-gcc-421-apple-build-56663-with-xcode-42/","url":"https://caiustheory.com/install-gcc-421-apple-build-56663-with-xcode-42/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eAs of Xcode 4.2 Apple have stopped bundling GCC with it, shipping only the (mostly) compatible llvm-gcc binary instead. The suggested fix is to install GCC using the \u003ca href=\"https://github.com/kennethreitz/osx-gcc-installer\"\u003eosx-gcc-installer\u003c/a\u003e project. However, I wanted to build and install it from source, which apple provides at \u003ca href=\"http://opensource.apple.com/\"\u003ehttp://opensource.apple.com/\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eYou should already have installed Xcode 4.2 from the app store, then basically the following steps are to grab the tarball from the \u003ca href=\"http://opensource.apple.com/release/developer-tools-41/\"\u003e4.1 developer tools source\u003c/a\u003e, unpack \u0026amp; compile it, then install it into the right places.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUpdate 2016-07-03:\u003c/strong\u003e I\u0026rsquo;d suggest just using homebrew to install this these days:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebrew install homebrew/dupes/apple-gcc42\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"instructions\"\u003eInstructions\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Grab and unpack the tarball\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir ~/tmp \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"nb\"\u003ecd\u003c/span\u003e ~/tmp\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecurl -O http://opensource.apple.com/tarballs/gcc/gcc-5666.3.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003etar zxf gcc-5666.3.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e gcc-5666.3\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Setup some stuff it requires\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emkdir -p build/obj build/dst build/sym\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# And then build it. You should go make a cup of tea or five whilst this runs.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egnumake install \u003cspan class=\"nv\"\u003eRC_OS\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003emacos \u003cspan class=\"nv\"\u003eRC_ARCHS\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;i386 x86_64\u0026#39;\u003c/span\u003e \u003cspan class=\"nv\"\u003eTARGETS\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;i386 x86_64\u0026#39;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  \u003cspan class=\"nv\"\u003eSRCROOT\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003e\u003cspan class=\"nb\"\u003epwd\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003e \u003cspan class=\"nv\"\u003eOBJROOT\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003e\u003cspan class=\"nb\"\u003epwd\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003e/build/obj \u003cspan class=\"nv\"\u003eDSTROOT\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003e\u003cspan class=\"nb\"\u003epwd\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003e/build/dst \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  \u003cspan class=\"nv\"\u003eSYMROOT\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003e\u003cspan class=\"nb\"\u003epwd\u003c/span\u003e\u003cspan class=\"sb\"\u003e`\u003c/span\u003e/build/sym\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# And finally install it\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo ditto build/dst /\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd now you should have \u003ccode\u003egcc-4.2\u003c/code\u003e in your \u003ccode\u003e$PATH\u003c/code\u003e, available to build all the things that \u003ccode\u003ellvm-gcc\u003c/code\u003e fails to compile.\u003c/p\u003e\n"},{"title":"TweetSavr","date_published":"2011-10-22T12:05:39Z","date_modified":"2011-10-22T12:05:39Z","id":"https://caiustheory.com/tweetsavr/","url":"https://caiustheory.com/tweetsavr/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI\u0026rsquo;ve had a dream for a while. A simple webapp that takes the last tweet in a conversation and outputs that conversation in chronological order on a page you can link to forevermore. Occasionally I\u0026rsquo;ll google to see if anything new\u0026rsquo;s turned up, but they all seem to do far more, require the start and end tweets or are covered in ads.\u003c/p\u003e\n\u003cp\u003eSo one friday evening I just built it. It\u0026rsquo;s called \u003ca href=\"http://tweetsavr.com/\"\u003eTweetSavr\u003c/a\u003e. It\u0026rsquo;s very simple—to the point the error page is just a standard 500 server error page currently. It fetches, caches and displays a conversation, given just the last tweet in said conversation.\u003c/p\u003e\n\u003cp\u003eKISS extends to the interface as well, I\u0026rsquo;m quite a fan of \u003ca href=\"http://jerz.setonhill.edu/writing/etext/url-hacking.htm\"\u003eURL hacking\u003c/a\u003e to use webapps, so TweetSavr works on that basis as well. The homepage sort of has some help telling you how to use it, but you basically take the (old-twitter) URL of the last tweet and paste it after tweetsavr.com in the address bar. Eg, \u003ca href=\"http://tweetsavr.com/http://twitter.com/ElizabethN/status/19766711653765120\"\u003ehttp://tweetsavr.com/http://twitter.com/ElizabethN/status/19766711653765120\u003c/a\u003e. It\u0026rsquo;ll then redirect you through to the actual page for that conversation. You can also put just the status id on the end of the URL, \u003ca href=\"http://tweetsavr.com/19766711653765120\"\u003ehttp://tweetsavr.com/19766711653765120\u003c/a\u003e and hey presto, it loads.\u003c/p\u003e\n\u003cp\u003eThe caching layer is moderately rudimentary, after fetching a tweet that isn\u0026rsquo;t in the cache it writes out a hash of data for that tweet into a yaml file. And when looking up a tweet it checks to see if that file exists, reading it in from disk if it is. Bonus side-effect is it builds up a corpus of tweets as yaml files on disk.\u003c/p\u003e\n\u003cp\u003eIt lives on the internet at \u003ca href=\"http://tweetsavr.com/\"\u003ehttp://tweetsavr.com/\u003c/a\u003e and the source is on github at \u003ca href=\"http://github.com/caius/tweetsavr\"\u003ehttp://github.com/caius/tweetsavr\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eSide note: isn\u0026rsquo;t it wonderful what we can create given just a few hours, a server somewhere in the cloud, and an idea? Never ceases to amaze me what can be built in just a short amount of time, even the dead simple things.\u003c/p\u003e\n"},{"title":"#to_param and keyword slugs","date_published":"2011-07-05T23:13:43Z","date_modified":"2011-07-05T23:13:43Z","id":"https://caiustheory.com/to_param-and-keyword-slugs/","url":"https://caiustheory.com/to_param-and-keyword-slugs/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eImagine you\u0026rsquo;ve got a blogging app and it\u0026rsquo;s currently generating URL paths like \u003ccode\u003eposts/10\u003c/code\u003e for individual posts. You decide the path should contain the post title (in some form) to make your URLs friendlier when someone reads them. I know I certainly prefer to read \u003ca href=\"http://caiustheory.com/abusing-ruby-19-and-json-for-fun\"\u003ehttp://caiustheory.com/abusing-ruby-19-and-json-for-fun\u003c/a\u003e vs \u003ca href=\"http://caiustheory.com/?id=70\"\u003ehttp://caiustheory.com/?id=70\u003c/a\u003e. \u003cem\u003e(That\u0026rsquo;s a fun blog post if you\u0026rsquo;re into (ab)using ruby occasionally!)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eNow you know \u003cem\u003eall\u003c/em\u003e about how to change the URL path that rails generates—just define \u003ccode\u003eto_param\u003c/code\u003e in your app. Something simple that generates a slug consisting of hyphens and lowercase alphanumerical characters. For example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 70-abusing-ruby-1-9-json-for-fun\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eto_param\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"nb\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e-\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egsub\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sr\"\u003e/\\W/\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;-\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esqueeze\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;-\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edowncase\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003e\u003cstrong\u003eNB\u003c/strong\u003e: You might want to go the route of storing the slug against the post record in the database and thus generating it before saving the record. In which case the rest of this post is sort of moot and you just need to search on that column. If not, then read on!\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eNow we\u0026rsquo;re generating a nice human-readable URL we need to change the way we find the post in the controller\u0026rsquo;s show action. Up until now it\u0026rsquo;s been a simple \u003ccode\u003e@post = Post.find(params[:id])\u003c/code\u003e to grab the record out the database. Problem now is \u003ccode\u003eparams[:id]\u003c/code\u003e is \u003ccode\u003e\u0026quot;70-abusing-ruby-1-9-json-for-fun\u0026quot;\u003c/code\u003e, rather than just \u003ccode\u003e\u0026quot;70\u0026quot;\u003c/code\u003e. A quick check in the \u003ca href=\"http://ruby-doc.org/core/classes/String.html#M001149\"\u003eString#to_i\u003c/a\u003e docs reveals it \u0026ldquo;Returns the result of interpreting leading characters in str as an integer base base (between 2 and 36).\u0026rdquo; Basically it extracts the first number it comes across and returns it.\u003c/p\u003e\n\u003cp\u003eKnowing that we can just lean on it to extract the id before using find to look for the post: \u003ccode\u003e@post = Post.find(params[:id].to_i)\u003c/code\u003e. Fantastic! We\u0026rsquo;ve got nice human readable paths on our blog posts and they can be found in the database. All finished… or are we?\u003c/p\u003e\n\u003cp\u003eThere\u0026rsquo;s still a rather embarassing bug in our code where we\u0026rsquo;re not explicitly checking the slug in the URL against the slug of the Post we\u0026rsquo;ve extracted from the database. If we visited \u003ccode\u003e/posts/70-ruby-19-sucks-and-python-rules-4eva\u003c/code\u003e it would load the blog post and render it without batting an eyelid. This has caused \u003ca href=\"http://www.niemanlab.org/2011/04/how-url-spoofing-can-put-libelous-words-into-news-orgs-mouths/\"\u003erather a few embarrassing situations\u003c/a\u003e for some high profile media outlets who don\u0026rsquo;t (or didn\u0026rsquo;t) check their URLs and just output the content. Luckily there\u0026rsquo;s a simple way for us to check this.\u003c/p\u003e\n\u003cp\u003eAll we want to do is render the content if the id param matches the slug of the post exactly, and return a 404 page if it doesn\u0026rsquo;t. We already know the id param (\u003ccode\u003eparams[:id]\u003c/code\u003e) and have pulled the Post object out of the database and stored it in an instance variable (\u003ccode\u003e@post\u003c/code\u003e). The \u003ccode\u003e@post\u003c/code\u003e knows how to generate it\u0026rsquo;s own slug, using \u003ccode\u003e#to_param\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eSo we end up with something like the following in our posts controller, which does all the above and correctly returns a 404 if someone enters an invalid slug (even if it starts with a valid post id):\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@post\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003ePost\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003eto_i\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003erender_404\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003eunless\u003c/span\u003e \u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"vi\"\u003e@post\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_param\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003erender_404\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003erender\u003c/span\u003e \u003cspan class=\"ss\"\u003e:file\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"no\"\u003eRails\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eroot\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;public/404.html\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:status\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"ss\"\u003e:not_found\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd going to an invalid path like \u003ccode\u003e/posts/70-ruby-19-sucks-and-python-rules-4eva\u003c/code\u003e just renders the default rails 404 page with a 404 HTTP status. (If you want the id to appear at the end of the path, alter \u003ccode\u003eto_param\u003c/code\u003e accordingly and do something like \u003ccode\u003eparams[:id].match(/\\d+$/)\u003c/code\u003e to extract the Post\u0026rsquo;s id to search on.)\u003c/p\u003e\n\u003cp\u003eHey presto, we\u0026rsquo;ve implemented human readable slugs that are tamper-proof (without storing them in the database.)\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e(And bonus points if in fact you spotted I used my blog as an example, but that it isn\u0026rsquo;t a rails app. (Nor contains the blog post ID in the pretty URL.) It\u0026rsquo;s actually powered by \u003ca href=\"http://habariproject.org/\"\u003eHabari\u003c/a\u003e at the time of posting!\u003c/em\u003e\u003c/p\u003e\n"},{"title":"Bad Recruiters - Rhys Evans at Devonshire","date_published":"2011-02-09T14:03:49Z","date_modified":"2011-02-09T14:03:49Z","id":"https://caiustheory.com/bad-recruiters-rhys-evans-at-devonshire/","url":"https://caiustheory.com/bad-recruiters-rhys-evans-at-devonshire/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eThis is a linked-in invitation I received from Rhys, and my reply.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eUpdate 2011-02-10:\u003c/strong\u003e As much as recruiters can be scummy twats, Rhys appears to at least care somewhat about his relationship with potential clients/contacts and has \u003ca href=\"http://caiustheory.com/bad-recruiters-rhys-evans-at-devonshire#comment-34098\"\u003eresponded in the comments\u003c/a\u003e. Normally my policy with recruiters is a two strike one, first email gets a polite \u0026ldquo;No thanks, go away.\u0026rdquo;, second gets a mini-rant to bugger off and stop contacting me. Rhys hadn\u0026rsquo;t technically contacted me before, but the unsolicited xmas email showed up when I searched my mailbox (which had annoyed me back when I received it.) And asking amongst my peers around at the time showed he was fairly disliked. (As you can see in some of the comments left below as well.) Since then, including a comment left below, a few people I trust have noted he\u0026rsquo;s really not that bad as recruiters go, and the fact he\u0026rsquo;s left a comment acknowledging perhaps his approach is a little misguided is enough for me to see he does still care about trying to be better than the rest of the recruitment crowd.\u003c/p\u003e\n\u003cp\u003eI still stand by my initial reply to him, and all other recruiters who don\u0026rsquo;t understand \u0026ldquo;No.\u0026rdquo; however.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eOn 02/09/11 5:30 AM, Rhys Evans wrote:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eHi Caius,\u003c/p\u003e\n\u003cp\u003eGood afternoon, I hope all is well.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve noticed we are connected to a number of the same people within the Rails space on LinkedIn. I\u0026rsquo;ve tried you on 07960 268100 to no avail so I\u0026rsquo;d like to add you to my network and make contact.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eHi Rhys,\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve already got you blocked on twitter, so we\u0026rsquo;ve obviously run across each other in the past. You also appear to have sent me a Merry Xmas email from Devonshire, with no previous contact initiated by me. (I seem to remember a lot of my friends got those emails as well and we eventually worked out you\u0026rsquo;d scraped Github for them.)\u003c/p\u003e\n\u003cp\u003eNow it would appear you\u0026rsquo;re being more intrusive and hunting folk out on linked in, ignoring the fact that they are employed and have specifically set linked in to say they aren\u0026rsquo;t looking for new jobs currently. From asking around you harass a few of my friends, to the point of ringing one up recently to tell them you knew they\u0026rsquo;d changed jobs and where they were now working. If you\u0026rsquo;re going to spend time doing that much research then why not have the decency to not be a completely mannerless cunt and leave them alone when they request you to.\u003c/p\u003e\n\u003cp\u003eIt would also appear you\u0026rsquo;ve just blanket-spammed me and a few people in my peer group through linked in with the same request, again a pretty dumb thing to do. It\u0026rsquo;s as if you recruiters think we never talk to each other, and don\u0026rsquo;t realise how much you lot being a bunch of pestering spammy bastards taints developers against ever dealing with a recruiter.\u003c/p\u003e\n\u003cp\u003eSo no, I don\u0026rsquo;t think I do want to accept your invitation to connect. And please never phone, email or contact me via any other means. I\u0026rsquo;m happily employed and if I \u003cem\u003eever\u003c/em\u003e need the services of a recruiter I\u0026rsquo;ll find someone who actually possesses an ounce of politeness about approaching (potential) candidates.\u003c/p\u003e\n\u003cp\u003eThanks,\u003cbr\u003e\nCaius\u003c/p\u003e\n"},{"title":"Abusing Ruby 1.9 \u0026 JSON for fun","date_published":"2011-02-07T19:16:58Z","date_modified":"2011-02-07T19:16:58Z","id":"https://caiustheory.com/abusing-ruby-19-and-json-for-fun/","url":"https://caiustheory.com/abusing-ruby-19-and-json-for-fun/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eEver since I found out about the new hash syntax you can use in ruby 1.9, and how similar that syntax is to JSON, I\u0026rsquo;ve been waiting for someone to realise you can just abuse \u003ccode\u003eeval()\u003c/code\u003e for parsing (some) JSON now.\u003c/p\u003e\n\u003cp\u003eFor example, lets say we have the following ruby hash, which could be generated by a RESTful api:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ething\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"ss\"\u003e:person\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"ss\"\u003e:name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;caius\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we pull in the JSON gem and dump that out as a string, we get the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ejsonstr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ething\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_json\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#39;{\u0026#34;person\u0026#34;:{\u0026#34;name\u0026#34;:\u0026#34;caius\u0026#34;}}\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat\u0026rsquo;s… not quite what we wanted. It\u0026rsquo;s not going to turn back into valid ruby as it is. Luckily javascript will parse objects without requiring the attributes to be wrapped in quotes, eg: \u003ccode\u003e{some: \u0026quot;attribute\u0026quot;}\u003c/code\u003e. We could build a JSON emitter that does it properly, or we could just run it through a regular expression instead. \u003cem\u003e(Lets also add a space after the colon to aid readability.)\u003c/em\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ejsonstr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egsub!\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sr\"\u003e/\u0026#34;([^\u0026#34;]+)\u0026#34;: /\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;\\1: \u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#39;{person: {name: \u0026#34;caius\u0026#34;}}\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOkay, so now we\u0026rsquo;ve turned a ruby hash into a JSON hash, that can still be parsed by the browser. Here\u0026rsquo;s a screenshot to prove that:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://farm6.static.flickr.com/5300/5425314597_43be5824cf_o.jpg\" alt=\"Valid JSON \u0026rsquo;thing\u0026rsquo;\"\u003e\u003c/p\u003e\n\u003cp\u003eAs you can see, it parses that into a valid JS object, complete with person and then (nested) name attributes. If we wanted to, \u003ccode\u003ething[\u0026quot;person\u0026quot;][\u0026quot;name\u0026quot;]\u003c/code\u003e or \u003ccode\u003ething.person.name\u003c/code\u003e would access the nested value \u0026ldquo;caius\u0026rdquo; just fine.\u003c/p\u003e\n\u003cp\u003eNow then, we\u0026rsquo;ve proved that is successfully parsed into javascript objects by the browser, generated from a ruby hash. No great shakes there, that\u0026rsquo;s fairly simple and has worked for ages. Now for my next trick, I\u0026rsquo;m going to turn that string of JSON back into a ruby hash, all without going anywhere near the JSON gem.\u003c/p\u003e\n\u003cp\u003eSome of you might have guessed what I\u0026rsquo;m about to do and have started hoping you\u0026rsquo;ve guessed wrongly — just for the record I don\u0026rsquo;t condone doing this except for fun and games. The JSON gem is there for a reason ;) With that little disclaimer out the way, here we go!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ething2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eeval\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ejsonstr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; {:person=\u0026gt;{:name=\u0026gt;\u0026#34;caius\u0026#34;}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ething2\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003ething\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; true\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOh snap! We just turned javascript objects back into valid ruby objects, in one simple method call. And we\u0026rsquo;d be able to access the \u0026ldquo;caius\u0026rdquo; value by calling \u003ccode\u003ething2[:person][:name]\u003c/code\u003e, or creating OpenStructs from the hashes and calling \u003ccode\u003ething2.person.name\u003c/code\u003e. Which is uncannily like the JS!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUpdated 2011-02-07\u003c/strong\u003e: \u003ca href=\"http://jens.ayton.se/\"\u003eJens Ayton\u003c/a\u003e pointed out unquoted keys aren\u0026rsquo;t strictly valid JSON, which is correct. Amended to say they\u0026rsquo;re parsed as javascript objects instead, with no mention of it being valid JSON.\u003c/p\u003e\n"},{"title":"App Store Hidden Preferences","date_published":"2011-01-06T19:45:49Z","date_modified":"2011-01-06T19:45:49Z","id":"https://caiustheory.com/app-store-hidden-preferences/","url":"https://caiustheory.com/app-store-hidden-preferences/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cem\u003eSee the Update at the end before you get excited :(\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eHaving just installed 10.6.6 to use the Mac App Store, I was \u003ca href=\"http://twitter.com/Caius/status/23096911170899968\"\u003eslightly annoyed\u003c/a\u003e that it fills my dock with apps as I install them. I\u0026rsquo;m a bit strange, in that I use a hidden preference to make the dock uneditable (it stops me accidentally dragging an app off.) But that means I can\u0026rsquo;t drag off the Mac App Store installed apps either.\u003c/p\u003e\n\u003cp\u003eHad a quick look through \u003ccode\u003e/Applications/App Store.app/Contents/MacOS/App Store\u003c/code\u003e with \u003ccode\u003estrings\u003c/code\u003e (love that tool) and noted a few strings that looked interesting. (There\u0026rsquo;s a full list \u003ca href=\"https://gist.github.com/768442\"\u003ein this gist\u003c/a\u003e.) There wasn\u0026rsquo;t anything that explicitly stated it stopped it putting anything in the dock, but I did notice an option that stopped it showing \u003cstrong\u003einstall progress\u003c/strong\u003e in the dock.\u003c/p\u003e\n\u003cp\u003eYank up a terminal window, bash out the following\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edefaults write com.apple.appstore FRDebugShowInstallProgress -bool NO\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u0026hellip;head back to the MAS and install another (free) app, and hey presto, it\u0026rsquo;s leaving my dock alone! Hopefully that\u0026rsquo;s all I needed to continue using my Dock as I like. (Hidden, and left alone.)\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUpdate 2011-01-06:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eSeems my joy was short-lived. I\u0026rsquo;d re-downloaded an app I\u0026rsquo;d already purchased and it just showed download progress in the MAS app, not in the dock. Installing new applications still shows up in the dock (annoyingly.)\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve been having a poke through how it all hangs together, and if it\u0026rsquo;s possible to actually block downloads from the Dock or not. It doesn\u0026rsquo;t look like there\u0026rsquo;s a hidden preference to hide new apps from downloading in the dock, you can just disable the progress bars in the dock with prefs. The MAS.app seems to be codenamed \u0026ldquo;Firenze\u0026rdquo;, with \u003ca href=\"https://gist.github.com/768829\"\u003ethe \u0026ldquo;hidden\u0026rdquo; prefs\u003c/a\u003e being prefixed with \u0026ldquo;FRDebug\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eAs I understand it, the App\\ Store.app invokes a binary inside \u003ccode\u003e/System/Library/PrivateFrameworks/CommerceKit.framework\u003c/code\u003e called \u0026ldquo;storeagent\u0026rdquo; to do the actual downloading/talking to the dock. From looking at the \u003ca href=\"https://gist.github.com/768837\"\u003eclass-dump\u003c/a\u003e of storeagent it communicates with the dock to place a new type of DockTile. Interesting sounding methods to (potentially?) swizzle are \u003ccode\u003e-[DownloadQueue sendDownloadListToDock]\u003c/code\u003e and \u003ccode\u003e-[DownloadQueue tellDockToAddDownload:]\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve given up for now, but I reckon it should be possible to create a bundle that swizzles the right methods in storeagent to stop it placing the downloads on the Dock.\u003c/p\u003e\n"},{"title":"Facebook iPhone app Contact Sync isn't automatic","date_published":"2010-10-06T14:10:06Z","date_modified":"2010-10-06T14:10:06Z","id":"https://caiustheory.com/facebook-iphone-app-contact-sync-isnt-automatic/","url":"https://caiustheory.com/facebook-iphone-app-contact-sync-isnt-automatic/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eThere\u0026rsquo;s an article on the guardian about \u003ca href=\"http://www.guardian.co.uk/technology/blog/2010/oct/06/facebook-privacy-phone-numbers-upload\"\u003eprivate phone numbers being uploaded from facebook\u003c/a\u003e, and another over \u003ca href=\"http://www.techeye.net/security/facebook-takes-and-stores-data-numbers-from-your-iphone\"\u003etecheye.net on the same subject.\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eFirstly a quote from the techeye article:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eFacebook doesn\u0026rsquo;t warn users that they are uploading their phone\u0026rsquo;s address book to Facebook.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eAnd whilst the guardian article never says it happens automatically, it also doesn\u0026rsquo;t lay it out that you have to \u003cstrong\u003eexplicitly\u003c/strong\u003e enable that feature, \u003cstrong\u003eand\u003c/strong\u003e agree to the facebook app uploading the data.\u003c/p\u003e\n\u003cp\u003eI was pretty sure that facebook wouldn\u0026rsquo;t be grabbing all your contact information without telling you, if they did at all, and that both articles were just pure scaremongering. So I fired up the facebook iPhone app, headed into my friends list on there, clicked sync and got the following screen:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://farm5.static.flickr.com/4148/5056619687_973ae660cc_d.jpg\" alt=\"\"\u003e\u003cbr\u003e\n\u003ca href=\"http://www.flickr.com/photos/caius/5056619687/\"\u003eView Original\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eOk, so according to that text, they\u0026rsquo;re just pulling down profile images and profile links from facebook and putting them in your address book against your contacts. Seems fairly harmless so far. So I toggled the top switch, to enable Contact Sync, and got the following screen:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://farm5.static.flickr.com/4145/5056697193_252e954ec3_d.jpg\" alt=\"\"\u003e\u003cbr\u003e\n\u003ca href=\"http://www.flickr.com/photos/caius/5056697193/\"\u003eView Original\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eReading that, it\u0026rsquo;s fairly obvious what data facebook are uploading (although a little ambiguous why), and it \u003cstrong\u003ecertainly\u003c/strong\u003e isn\u0026rsquo;t happening automatically. As it says, it uploads the \u0026ldquo;name, email address, phone number\u0026rdquo; from all your contacts to facebook, and pull down \u0026ldquo;your friends\u0026rsquo; profile photos and other info from Facebook\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eSo whilst facebook \u003cem\u003eare\u003c/em\u003e collecting more data (albeit stored subject to their Privacy Policy), it\u0026rsquo;s certainly \u003cstrong\u003eNOT\u003c/strong\u003e done automatically, and is \u003cstrong\u003every explicit\u003c/strong\u003e about what is being uploaded at the point you enable it.\u003c/p\u003e\n"},{"title":"+[NSObject load] in MacRuby","date_published":"2010-06-12T02:11:32Z","date_modified":"2010-06-12T02:11:32Z","id":"https://caiustheory.com/nsobject-load-in-macruby/","url":"https://caiustheory.com/nsobject-load-in-macruby/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cem\u003eIf you\u0026rsquo;ve not heard of it, \u003ca href=\"http://www.macruby.org/\"\u003eMacRuby\u003c/a\u003e is \u003c!-- raw HTML omitted --\u003ean implementation of Ruby 1.9 directly on top of Mac OS X core technologies such as the Objective-C runtime and garbage collector, the LLVM compiler infrastructure and the Foundation and ICU frameworks.\u003c!-- raw HTML omitted --\u003e Basically means you write in Ruby using Objective-C frameworks, and vice versa. It\u0026rsquo;s pretty damn cool to be honest!\u003c/em\u003e\u003c/p\u003e\n\u003ch3 id=\"what-is-nsobject-load\"\u003eWhat is +[NSObject load]?\u003c/h3\u003e\n\u003cp\u003eFrom the \u003ca href=\"http://developer.apple.com/mac/library/documentation/cocoa/reference/foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/occ/clm/NSObject/load\"\u003edocumentation\u003c/a\u003e:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eInvoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eThis means when your class is loaded, and implements the \u003ccode\u003eload\u003c/code\u003e method, you get a \u003ccode\u003eload\u003c/code\u003e message sent to your class. Which means you can start doing stuff as soon as your class is loaded by the runtime.\u003c/p\u003e\n\u003cp\u003eThe main place I\u0026rsquo;ve seen it used (and used it myself) is in \u003ca href=\"http://www.culater.net/software/SIMBL/SIMBL.php\"\u003eSIMBL\u003c/a\u003e plugins. A SIMBL plugin is an NSBundle that contains code which is loaded (injected) into a running application shortly after said application is launched. It lets you extend (or \u0026ldquo;fix\u0026rdquo;) cocoa applications with additional features. So you have this bundle of code, that gets loaded into a running application some point after it starts, and you want to run some code as the bundle is loaded - usually to kick off doing whatever you want to do in the plugin. This is where \u003ccode\u003eload\u003c/code\u003e becomes useful.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s a quick implementation that just logs to the console:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-objc\" data-lang=\"objc\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003e@implementation\u003c/span\u003e \u003cspan class=\"nc\"\u003eMainController\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003eload\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eNSlog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e@\u0026#34;MainController#load called\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"now-where-does-macruby-come-into-this\"\u003eNow where does MacRuby come into this?\u003c/h3\u003e\n\u003cp\u003eWell I came across a need to do the same in ruby, have some code triggered when the class is loaded into the runtime. Tried implementing \u003ccode\u003eClass.load\u003c/code\u003e but to no avail. Then remembered MacRuby is just ruby! And I can call any code from within my ruby class definition.\u003c/p\u003e\n\u003cp\u003eFor continuity I still call it \u003ccode\u003eClass.load\u003c/code\u003e, but then call it as soon as I\u0026rsquo;ve defined it in the class. Eg:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMainController\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nc\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eload\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"no\"\u003eNSLog\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;MainController#load called\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eload\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOf course, I\u0026rsquo;m not sure when the Objective-C method is called, it\u0026rsquo;s probably after the entire class has been defined rather than as soon as \u003ccode\u003eload\u003c/code\u003e has been loaded into the runtime. So you might want to move the \u003ccode\u003eself.load\u003c/code\u003e call to just before the closing \u003ccode\u003eend\u003c/code\u003e.\u003c/p\u003e\n"},{"title":"Potty Training YAML","date_published":"2010-05-10T21:46:17Z","date_modified":"2010-05-10T21:46:17Z","id":"https://caiustheory.com/potty-training-yaml/","url":"https://caiustheory.com/potty-training-yaml/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eRan into a problem today where I have a class with a few attributes on it, but I only want a certain three of those attributes to appear in the YAML dump of a class instance.\u003c/p\u003e\n\u003cp\u003eDiving straight into a code example\u0026ndash;lets say we have a \u003ccode\u003eContact\u003c/code\u003e class, and we only want to dump the \u003ccode\u003ename\u003c/code\u003e, \u003ccode\u003eemail\u003c/code\u003e and \u003ccode\u003ewebsite\u003c/code\u003e attributes.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;yaml\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eContact\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kp\"\u003eattr_accessor\u003c/span\u003e \u003cspan class=\"ss\"\u003e:name\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:email\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:website\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:telephone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e# helper method to make setting up easy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003einitialize\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"p\"\u003e{})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003evalue\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003emeffod\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_s\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e=\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nb\"\u003esend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emeffod\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003erespond_to?\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emeffod\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# And create an instance for us to play with\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecaius\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eContact\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"ss\"\u003e:name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Caius\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"ss\"\u003e:email\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;dev@caius.name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"ss\"\u003e:website\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;http://caius.name/\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"ss\"\u003e:telephone\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;12345\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAs we\u0026rsquo;d expect when dumping this, all instance variables get dumped out:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003ecaius\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_yaml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; --- !ruby/object:Contact \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; email: dev@caius.name\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; name: Caius\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; telephone: \u0026#34;12345\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; website: http://caius.name/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInitially I tried to override \u003ccode\u003eto_yaml\u003c/code\u003e and unset the instance variables I didn\u0026rsquo;t want showing up, but that just made them show up empty. After digging around a bit more, I happened across the \u003ca href=\"http://yaml4r.sourceforge.net/doc/page/type_families.htm\"\u003eType Families\u003c/a\u003e page in the yaml4r docs, which right at the bottom mentions \u003ccode\u003eto_yaml_properties\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eTurns out \u003ccode\u003eto_yaml_properties\u003c/code\u003e returns an array of instance variable names (as strings) that should be dumped out as part of the object. A quick method definition later, and we\u0026rsquo;re only dumping the variables we want. (\u003cem\u003eSee my \u003ca href=\"http://caiustheory.com/ruby-shortcuts\"\u003eRuby Shortcuts\u003c/a\u003e post if you don\u0026rsquo;t know what \u003ccode\u003e%w()\u003c/code\u003e does\u003c/em\u003e)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eContact\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eto_yaml_properties\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"sx\"\u003e%w(@name @email @website)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd now we dump the class, expecting only the three attributes to be outputted:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003ecaius\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_yaml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; --- !ruby/object:Contact \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; name: Caius\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; email: dev@caius.name\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; website: http://caius.name/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSuccess!\u003c/p\u003e\n"},{"title":"Ruby Shortcuts","date_published":"2010-03-18T22:32:45Z","date_modified":"2010-03-18T22:32:45Z","id":"https://caiustheory.com/ruby-shortcuts/","url":"https://caiustheory.com/ruby-shortcuts/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eThere\u0026rsquo;s a few useful shorthand ways to create certain objects in Ruby, a couple of obvious ones are \u003ccode\u003e[]\u003c/code\u003e to create an \u003ccode\u003eArray\u003c/code\u003e and \u003ccode\u003e{}\u003c/code\u003e to create a \u003ccode\u003eHash\u003c/code\u003e (Or block/\u003ccode\u003eProc\u003c/code\u003e). There\u0026rsquo;s some not so obvious ones too, for creating strings, regexes and executing shell commands.\u003c/p\u003e\n\u003cp\u003eWith all of the examples I\u0026rsquo;ve used \u003ccode\u003e{}\u003c/code\u003e as the delimiter characters, but you can use a variety of characters. Personally I tend to use \u003ccode\u003e{}\u003c/code\u003e unless the string contains them, in which case I\u0026rsquo;ll use \u003ccode\u003e//\u003c/code\u003e or \u003ccode\u003e@@\u003c/code\u003e. My only exception appears to be \u003ccode\u003e%w\u003c/code\u003e, for which I tend to use \u003ccode\u003e()\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"strings\"\u003eStrings\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e%\u003c/code\u003e and \u003ccode\u003e%Q\u003c/code\u003e are the same as using double quotes, including string interpolation. Really useful when you want to create a string that contains double quotes, but without the hassle of escaping them.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%{}\u003c/span\u003e                 \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%Q{}\u003c/span\u003e                \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%{caius}\u003c/span\u003e            \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;caius\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%{caius \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"sx\"\u003e}\u003c/span\u003e       \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;caius 5\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%{some \u0026#34;foo\u0026#34; thing}\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;some \\\u0026#34;foo\\\u0026#34; thing\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003e%q\u003c/code\u003e is equivalent to using single quotes. Behaves exactly the same, no string interpolation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%q{}\u003c/span\u003e           \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#39;\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%q{caius}\u003c/span\u003e      \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;caius\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%q{caius #{5}}\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;caius \\#{5}\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"arrays\"\u003eArrays\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e%w\u003c/code\u003e is the equivalent of using String#split. It takes a string and splits it on whitespace. With the added bonus of being able to escape whitespace too. \u003ccode\u003e%W\u003c/code\u003e allows string interpolation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%w(foo bar sed)\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# =\u0026gt; [\u0026#34;foo\u0026#34;, \u0026#34;bar\u0026#34;, \u0026#34;sed\u0026#34;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%w(foo\\ bar sed)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; [\u0026#34;foo bar\u0026#34;, \u0026#34;sed\u0026#34;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%W(foo \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"sx\"\u003e bar)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; [\u0026#34;foo\u0026#34;, \u0026#34;5\u0026#34;, \u0026#34;bar\u0026#34;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"regexes\"\u003eRegexes\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e%r\u003c/code\u003e is just like using \u003ccode\u003e//\u003c/code\u003e to create a regexp object. Comes in handy when you\u0026rsquo;re writing a regex containing \u003ccode\u003e/\u003c/code\u003e as you don\u0026rsquo;t have to continually escape it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003e%r{foo|bar}\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; /foo|bar/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003e%r{foo/bar}\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; /foo\\/bar/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"symbols\"\u003eSymbols\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e%s\u003c/code\u003e creates a symbol, just like writing \u003ccode\u003e:foo\u003c/code\u003e manually. It takes care of escaping the symbol, but unlike \u003ccode\u003e:\u0026quot;\u0026quot;\u003c/code\u003e it doesn\u0026rsquo;t allow string interpolation however.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%s{foo}\u003c/span\u003e      \u003cspan class=\"c1\"\u003e# =\u0026gt; :foo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%s{foo/bar}\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# =\u0026gt; :\u0026#34;foo/bar\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"ss\"\u003e:\u0026#34;foo-\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e\u0026#34;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# =\u0026gt; :\u0026#34;foo-5\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%s{foo-#{5}}\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; :\u0026#34;foo-\\#{5}\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"shelling-out\"\u003eShelling out\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e%x\u003c/code\u003e is the same as backticks (\u003c!-- raw HTML omitted --\u003e``\u003c!-- raw HTML omitted --\u003e), executes the command in a shell and returns the output as a string. And just like backticks it supports string interpolation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sb\"\u003e`echo hi`\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;hi\\n\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%x{echo hi}\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;hi\\n\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sx\"\u003e%x{echo \u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"sx\"\u003e}\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;5\\n\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"iPad? iPerfect (…for me)","date_published":"2010-01-27T20:56:50Z","date_modified":"2010-01-27T20:56:50Z","id":"https://caiustheory.com/ipad-iperfect-for-me/","url":"https://caiustheory.com/ipad-iperfect-for-me/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cem\u003eGoogle Groups is a pile of fail and hasn\u0026rsquo;t posted my message in reply to a thread on \u003ca href=\"http://geekup.org/\"\u003eGeekup\u003c/a\u003e so I\u0026rsquo;m blogging it instead.\u003c/em\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eOn 27 Jan 2010, at 19:53, Steve Richardson wrote:\u003cbr\u003e\nThoughts?\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eI\u0026rsquo;ve been searching for a device to fit between my Macbook Pro and iPhone. I work all day on the MBP, and moving it to then watch video in another room or read twitter/news/mail whilst watching telly, etc is a pain.\u003c/p\u003e\n\u003cp\u003eThe iPhone is a great little device on the move, but for trying to multitask at home it\u0026rsquo;s a bit.. tedious. Even jailbroken and running multiple apps at once it\u0026rsquo;s still limiting.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;d been looking around at netbooks, but what put me off actually getting one was my previous experience with one. I know I\u0026rsquo;d want it to run OS X to keep in sync (easily) with my other Apple devices, but hackintoshing one was a bit too much hassle, plus the fact ones to hackintosh cost more than I really wanted to pay for something that wasn\u0026rsquo;t \u003cem\u003equite\u003c/em\u003e what I thought I needed.\u003c/p\u003e\n\u003cp\u003eAnd then.. the iPad. I\u0026rsquo;ve been sort of keeping up with the rumours (mainly through \u003ca href=\"http://daringfireball.net\"\u003eDaring Fireball\u003c/a\u003e) and whilst I didn\u0026rsquo;t get excited about it too much ahead of announcement\u003c!-- raw HTML omitted --\u003e1\u003c!-- raw HTML omitted --\u003e, having seen the official video of it it\u0026rsquo;s pretty much guaranteed that I\u0026rsquo;m going to get one.\u003c/p\u003e\n\u003cp\u003eYes, it\u0026rsquo;s limited (App Store, closed device), but.. I don\u0026rsquo;t care. Take the iPhone, it\u0026rsquo;s good enough for doing things on it, even if someone else is in charge of the ecosystem and has a big finger saying yes or no. I (willingly) use iTunes, Mobile Me, all the things that are so wonderfully integrated in the world of Apple, so another device that consumes my media using channels I already know and use is just a massive win for me.\u003c/p\u003e\n\u003cp\u003eAll I\u0026rsquo;m hoping now is that $499 doesn\u0026rsquo;t equal £499. Hopefully it\u0026rsquo;ll be £399, still a good £80 above direct exchange rate, but low enough that it\u0026rsquo;s a no-brainer for me to get one.\u003c/p\u003e\n\u003cp\u003e…And I think this is the first Apple product that I\u0026rsquo;ve seen announced and actually known from the start \u003cem\u003ewhy\u003c/em\u003e I\u0026rsquo;m going to get one, instead of just a knee-jerk \u0026ldquo;SHINY!!!! WANT!!!\u0026rdquo; reaction. Uh oh, does that make me an adult?\u003c/p\u003e\n\u003cp\u003e\u003c!-- raw HTML omitted --\u003e1\u003c!-- raw HTML omitted --\u003e I miss getting really excited about apple announcements :(\u003c/p\u003e\n\u003ch4 id=\"update\"\u003eUpdate\u003c/h4\u003e\n\u003cp\u003eIt just got even better. Was lamenting to a \u003ca href=\"http://tmertz.com/\"\u003efriend\u003c/a\u003e on IM that it\u0026rsquo;d be so much nicer once you can directly suck photos off a camera/SD card into it. Turns out there\u0026rsquo;s an adapter for that. See \u0026ldquo;iPad Camera Connection Kit\u0026rdquo; at the bottom of \u003ca href=\"http://www.apple.com/ipad/specs/\"\u003ehttp://www.apple.com/ipad/specs/\u003c/a\u003e for details.\u003c/p\u003e\n"},{"title":"at(1) on OS X","date_published":"2009-12-28T09:30:30Z","date_modified":"2009-12-28T09:30:30Z","id":"https://caiustheory.com/at-1-on-os-x/","url":"https://caiustheory.com/at-1-on-os-x/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI recently came across the \u003ca href=\"http://developer.apple.com/mac/library/DOCUMENTATION/Darwin/Reference/ManPages/man1/at.1.html\"\u003e\u003ccode\u003eat(1)\u003c/code\u003e\u003c/a\u003e command, and wondered why it wasn\u0026rsquo;t executing jobs I gave it on my machine. Had a poke around the man pages, and discovered in \u003ca href=\"http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man8/atrun.8.html\"\u003e\u003ccode\u003eatrun(8)\u003c/code\u003e\u003c/a\u003e that by default \u003ccode\u003elaunchd(8)\u003c/code\u003e has the \u003ccode\u003eatrun\u003c/code\u003e entry disabled.\u003c/p\u003e\n\u003cp\u003eTo enable it (and have \u003ccode\u003eat\u003c/code\u003e jobs fire) you simply need to run the following command once:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePersonally I\u0026rsquo;ve taken to using this to sleep my machine after a custom amount of time, mainly because my alarm clock/sleep timer of choice (\u003ca href=\"http://embraceware.com/awaken/\"\u003eAwaken\u003c/a\u003e) can\u0026rsquo;t handle playing \u003ca href=\"http://www.spotify.com/\"\u003eSpotify\u003c/a\u003e for x minutes and then sleeping the machine. The following command puts the machine to sleep, which (quite effectively) silences spotify.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;osascript -e \u0026#39;tell app \\\u0026#34;Finder\\\u0026#34; to sleep\u0026#39;\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e at 1:00am\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSee the \u003ca href=\"http://developer.apple.com/mac/library/DOCUMENTATION/Darwin/Reference/ManPages/man1/at.1.html\"\u003e\u003ccode\u003eat(1)\u003c/code\u003e\u003c/a\u003e manpage for how to specify the time, but as I\u0026rsquo;m only ever scheduling it on the same day (usually 20 minutes or so in advance), just passing the time works fine.\u003c/p\u003e\n"},{"title":"Read Later in a keystroke","date_published":"2009-12-06T22:15:37Z","date_modified":"2009-12-06T22:15:37Z","id":"https://caiustheory.com/read-later-in-a-keystroke/","url":"https://caiustheory.com/read-later-in-a-keystroke/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI use a wonderful service for saving text to be read later, \u003ca href=\"http://instapaper.com/\"\u003einstapaper.com\u003c/a\u003e. It\u0026rsquo;s gotten more wonderful as time has gone on and other applications/service\u0026rsquo;s have gained the ability to save links/articles/webpages there for me to pick up later.\u003c/p\u003e\n\u003cp\u003eFor instance, I\u0026rsquo;m out and about checking twitter on my iPhone using \u003ca href=\"http://atebits.com/tweetie-iphone/\"\u003etweetie\u003c/a\u003e and someone tweets a link. Rather than wait for it to load and having to read it then and there I can just hit \u0026ldquo;Read Later\u0026rdquo; and it\u0026rsquo;s saved in my instapaper account for me to read as and when I choose to. Recently the legendary mac feed reader \u003ca href=\"http://www.newsgator.com/INDIVIDUALS/NETNEWSWIRE/\"\u003eNetNewsWire\u003c/a\u003e gained this ability too.\u003c/p\u003e\n\u003cp\u003eThere\u0026rsquo;s a few ways to send a feed item to instapaper from within NNW. Firstly you can right-click and click \u0026ldquo;Send to Instapaper\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://farm3.static.flickr.com/2553/4163576297_ee60e26b53_o.jpg\" alt=\"Send to Instapaper from contextual menu\"\u003e\u003cbr\u003e\n\u003ca href=\"http://www.flickr.com/photos/caius/4163576297\"\u003eView Original on Flickr\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eSecondly there\u0026rsquo;s a menu item for it in the News menu, which also provides my chosen way of instapapering an item—the keyboard shortcut! ⌃P \u003cem\u003e(control-P)\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://farm3.static.flickr.com/2748/4164341910_476f8ba539_o.jpg\" alt=\"Send to Instapaper from News menu\"\u003e\u003cbr\u003e\n\u003ca href=\"http://www.flickr.com/photos/caius/4164341910\"\u003eView Original on Flickr\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eSo, in NNW I\u0026rsquo;m happily sending stuff to instapaper with the handy ⌃P shortcut, but that doesn\u0026rsquo;t exist in the third place I mark things to read later\u0026ndash;Safari! Up until now I\u0026rsquo;ve been using the standard \u0026ldquo;Read Later\u0026rdquo; bookmarklet that \u003ca href=\"http://instapaper.com/\"\u003einstapaper.com\u003c/a\u003e provides, and it\u0026rsquo;s got a spot on my Bookmarks Bar so I can easily click it.\u003c/p\u003e\n\u003cp\u003eThat doesn\u0026rsquo;t really help with the fact I\u0026rsquo;m hitting ⌃P in NNW, and it doesn\u0026rsquo;t work in Safari. Quite often I noticed myself hitting the key combination in Safari and wondering for a split second why it wasn\u0026rsquo;t sending the item to instapaper. Then the solution hit me!\u003c/p\u003e\n\u003cp\u003eIn OS X you can setup (and/or override) menu items with custom key combinations! Why hadn\u0026rsquo;t I remembered this before. Because the \u0026ldquo;Read Later\u0026rdquo; bookmark*(let)* is nested under the Bookmarks menu, it \u003cstrong\u003eis\u003c/strong\u003e a menu item! A quick trip into the Keyboards Prefpane in System Preferences and a new binding later and voilâ, \u0026ldquo;Read Later\u0026rdquo; in Safari is bound to ⌃P and I can use it in both Safari and NNW.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://farm3.static.flickr.com/2517/4163642801_a14250da65_o.jpg\" alt=\"Filling in the form to bind the keyboard shortcut\"\u003e\u003cbr\u003e\n\u003ca href=\"http://www.flickr.com/photos/caius/4163642801\"\u003eView Original on Flickr\u003c/a\u003e\u003c/p\u003e\n"},{"title":"My Menubar Items","date_published":"2009-12-06T12:56:49Z","date_modified":"2009-12-06T12:56:49Z","id":"https://caiustheory.com/my-menubar-items/","url":"https://caiustheory.com/my-menubar-items/","author":{"name":"Caius Durling"},"content_html":"\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003cp\u003eThis is a something that occasionally makes the rounds again, I\u0026rsquo;ve not seen it for a while and I\u0026rsquo;ve added some new items since I last remember documenting it. Thus, \u003ca href=\"http://twitter.com/macarne\"\u003e@macarne\u003c/a\u003e \u003ca href=\"http://twitter.com/macarne/status/6398125336\"\u003easking\u003c/a\u003e what the app was that gives me stats prompted me to document my current menubar items.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://farm3.static.flickr.com/2586/4162170875_1d1a8be4cf.jpg\" alt=\"annotated-menubar by ©aius, on Flickr\" title=\"annotated-menubar\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://www.flickr.com/photos/caius/4162170875/\"\u003eView original\u003c/a\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca href=\"http://www.eidac.de/\"\u003eSMCFanControl\u003c/a\u003e - Lets me adjust the minimum speed of my fans.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://atebits.com/tweetie-mac\"\u003eTweetie/mac\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://iscrobbler.sourceforge.net/\"\u003eiScrobbler\u003c/a\u003e - Scrobbles tunes iTunes plays\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.realmacsoftware.com/littlesnapper/\"\u003eLittleSnapper\u003c/a\u003e \u003cem\u003e(Or more accurately the menubar icon is NanoSnapper, LittleSnapper is the full app.)\u003c/em\u003e Mainly used for screen grabs.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.orange-carb.org/SBM/\"\u003eSlimBatteryMonitor\u003c/a\u003e - Takes up less horizontal space than Apple\u0026rsquo;s menu item.\u003c/li\u003e\n\u003cli\u003eExpresscard menu item - Lets me power off my \u003ca href=\"http://www.memoryc.com/storage/solidstatedisk/48gbfilematesolidgoexpresscardultra.html\"\u003eExpresscard/34 SSD\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.ragingmenace.com/software/menumeters/\"\u003eMenuMeters\u003c/a\u003e - An old friend I\u0026rsquo;ve been using for as long as I can remember running OS X. Set to show (left to right)\n\u003col\u003e\n\u003cli\u003eRam - \u003cstrong\u003eU\u003c/strong\u003esed and \u003cstrong\u003eF\u003c/strong\u003eree totals.\u003c/li\u003e\n\u003cli\u003eNetwork - Graph + values.\u003c/li\u003e\n\u003cli\u003eCPU - Graph per core. Probably the most useful out of the three.\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003eBluetooth\u003c/li\u003e\n\u003cli\u003eTime Machine\u003c/li\u003e\n\u003cli\u003eModem - To dial on my Huawei E220 3G stick.\u003c/li\u003e\n\u003cli\u003eAirport\u003c/li\u003e\n\u003cli\u003eSound\u003c/li\u003e\n\u003cli\u003eDay/Time\u003c/li\u003e\n\u003cli\u003eFast User Switching - Not sure why I keep this in the menubar, only have one user and I lock my screen with a password protected screensaver.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.viscosityvpn.com/\"\u003eViscosity\u003c/a\u003e - VPN software. Pretty useful.\u003c/li\u003e\n\u003cli\u003eSpotlight! - Occasionally this vanishes when spotlight decides to be a dick and eat ram/cpu reindexing my disk every few hours. Touch wood it hasn\u0026rsquo;t done it since 10.6.1.\u003c/li\u003e\n\u003c/ol\u003e\n"},{"title":"Read standard input using Objective-C","date_published":"2009-12-06T11:50:08Z","date_modified":"2009-12-06T11:50:08Z","id":"https://caiustheory.com/read-standard-input-using-objective-c/","url":"https://caiustheory.com/read-standard-input-using-objective-c/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eOn a couple of occasions now I\u0026rsquo;ve wanted to read from \u003ccode\u003eSTDIN\u003c/code\u003e into an Objective-C command line tool, and both times I\u0026rsquo;ve had to hunt quite a bit to find the answer because nothing shows up in google for the search terms I used. \u0026ldquo;Objective-c read from stdin\u0026rdquo; and \u0026ldquo;objc read stdin\u0026rdquo; both turn up results ranging from using \u003ccode\u003eNSInputStream\u003c/code\u003e to dropping some C++ in there.\u003c/p\u003e\n\u003cp\u003eThe answer is quite simple really, just use \u003ccode\u003eNSFileHandle\u003c/code\u003e. More specifically \u003ccode\u003e+[NSFileHandle fileHandleWithStandardInput]\u003c/code\u003e. You can then read all data currently in \u003ccode\u003eSTDIN\u003c/code\u003e, monitor it for new data and anything else you can do with a normal \u003ccode\u003eNSFileHandle\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAnd here\u0026rsquo;s some example code, reads all data from \u003ccode\u003eSTDIN\u003c/code\u003e and stores it into an NSString:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-objc\" data-lang=\"objc\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eNSFileHandle\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003einput\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eNSFileHandle\u003c/span\u003e \u003cspan class=\"n\"\u003efileHandleWithStandardInput\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eNSData\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003einputData\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eNSData\u003c/span\u003e \u003cspan class=\"nl\"\u003edataWithData\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003einput\u003c/span\u003e \u003cspan class=\"n\"\u003ereadDataToEndOfFile\u003c/span\u003e\u003cspan class=\"p\"\u003e]];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eNSString\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003einputString\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"n\"\u003eNSString\u003c/span\u003e \u003cspan class=\"n\"\u003ealloc\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nl\"\u003einitWithData\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003einputData\u003c/span\u003e \u003cspan class=\"nl\"\u003eencoding\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eNSUTF8StringEncoding\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eI\u0026rsquo;m using this in GarbageCollected apps, memory management without GC is left as an exercise to the user.\u003c/em\u003e\u003c/p\u003e\n"},{"title":"Nissan Almera Self Diagnostic Menu","date_published":"2009-09-28T10:44:00Z","date_modified":"2009-09-28T10:44:00Z","id":"https://caiustheory.com/nissan-almera-self-diagnostic-menu/","url":"https://caiustheory.com/nissan-almera-self-diagnostic-menu/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eHere\u0026rsquo;s how to access the self diagnostic / configuration menu on a Nissan Almera 2003 SVE (N16):\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eStart the engine\u003c/li\u003e\n\u003cli\u003eTurn the radio on\u003c/li\u003e\n\u003cli\u003eTurn the radio off\u003c/li\u003e\n\u003cli\u003eHold the info button in \u003cem\u003ethen:\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003eTurn the volume knob up (clockwise) \u003cem\u003euntil:\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003eDiagnostic menu appears\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eFrom here you can do various things: run self-diagnostics; reset/change the main service counter; various other tests for the climate control, sat nav system, etc.\u003c/p\u003e\n"},{"title":"Ignore .gitignore in Git","date_published":"2009-09-21T06:00:00Z","date_modified":"2009-09-21T06:00:00Z","id":"https://caiustheory.com/ignore-gitignore-in-git/","url":"https://caiustheory.com/ignore-gitignore-in-git/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eRecently I ran into an issue where I was working on a project which had files I wanted git to ignore, but I didn\u0026rsquo;t want to commit a \u003ccode\u003e.gitignore\u003c/code\u003e file into the project. In case you don\u0026rsquo;t know, any files matching a pattern in \u003ccode\u003e.gitignore\u003c/code\u003e in a git repository are ignored by git. (Unless the file(s) have already been committed, then they need removing from git before they are ignored.)\u003c/p\u003e\n\u003cp\u003eInitially I figured I could just throw the patterns I needed excluded into my global \u003ccode\u003e~/.gitignore\u003c/code\u003e, but quickly realised that I needed files matching these patterns to show up in other git repos, so going the global route really wasn\u0026rsquo;t an option. After some thought I wondered if you could make git ignore \u003ccode\u003e.gitignore\u003c/code\u003e, whilst still getting it to ignore files matching the other patterns in the \u003ccode\u003e.gitignore\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eLets create a new empty repo to test this crazy idea in:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ mkdir foo\n$ cd foo\n$ git init\nInitialized empty Git repository in /Volumes/Brutus/Users/caius/foo/.git/\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd create a couple of files for us to play with:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ touch bar\n$ touch baz\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIgnore one of the files so we can check other matches are still ignored later on:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ echo \u0026quot;baz\u0026quot; \u0026gt;\u0026gt; .gitignore\n$ git status\n# On branch master\n#\n# Initial commit\n#\n# Untracked files:\n#   (use \u0026quot;git add \u0026lt;file\u0026gt;...\u0026quot; to include in what will be committed)\n#\n#       .gitignore\n#       bar\nnothing added to commit but untracked files present (use \u0026quot;git add\u0026quot; to track)\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eOk so far, but we can still see .gitignore in git, so now for the crazy shindig, ignore the ignore file:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ echo \u0026quot;.gitignore\u0026quot; \u0026gt;\u0026gt; .gitignore \n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eLets see if it worked, or if we can still see our .gitignore:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ git status\n# On branch master\n#\n# Initial commit\n#\n# Untracked files:\n#   (use \u0026quot;git add \u0026lt;file\u0026gt;...\u0026quot; to include in what will be committed)\n#\n#       bar\nnothing added to commit but untracked files present (use \u0026quot;git add\u0026quot; to track)\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd lets just double-check that \u003ccode\u003e.gitignore\u003c/code\u003e and \u003ccode\u003ebaz\u003c/code\u003e still exist on the filesystem:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ ls -a\n.  ..  .git  .gitignore  bar  baz\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eFantastic! Turns out adding \u0026ldquo;.gitignore\u0026rdquo; to \u003ccode\u003e.gitignore\u003c/code\u003e works perfectly. The file is still parsed by git to ignore everything else too, so it does exactly what I needed in this instance.\u003c/p\u003e\n"},{"title":"Filter through command","date_published":"2009-09-14T22:39:37Z","date_modified":"2009-09-14T22:39:37Z","id":"https://caiustheory.com/filter-through-command/","url":"https://caiustheory.com/filter-through-command/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cem\u003eThis is another old post that I\u0026rsquo;m republishing. Originally published 27th April 2007.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eMy text editor \u003ca href=\"http://macromates.com/\"\u003eTextMate\u003c/a\u003e has a nice feature called \u0026ldquo;Filter through command\u0026rdquo; whereby you can filter the current document through a command.\u003c/p\u003e\n\u003cp\u003eAnyway, I\u0026rsquo;ve never used it before, but today I had a text file with 30 or so url\u0026rsquo;s in, each on a new line, so I thought I\u0026rsquo;d test it out.  I selected it to input the document \u0026amp; to not replace the output.  I then entered the following command, which is a ruby command to take each line that isn\u0026rsquo;t blank, and run the shell command \u003ccode\u003eopen $url\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eruby -e \u003cspan class=\"s1\"\u003e\u0026#39;a = ARGF.read.scan(/\\S+/); a.each { |x| `open #{x}` }\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhat this does is take ARGF (the document) and read it in line by line, but only the non-whitespace characters (so newlines, space, etc are ignored.)  And it assigns it to an array called \u003ccode\u003ea\u003c/code\u003e.  What I then do is for each item of \u003ccode\u003ea\u003c/code\u003e, we run it past the shell command \u003ccode\u003eopen\u003c/code\u003e, which on OS X if you pass it a URL it just opens that URL in the default browser.\u003c/p\u003e\n\u003cp\u003eMy browser is Safari, and its set to open new links in a new tab in the foremost window.  So I ran the command, and hey presto, within a few seconds I had all the URL\u0026rsquo;s loading in seperate tabs in Safari\u0026rsquo;s foremost window!\u003c/p\u003e\n\u003cp\u003eThe power of Unix \u003cem\u003e(OS X)\u003c/em\u003e \u0026amp; TextMate (amongst other tools) just never ceases to amaze me.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUpdate\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eI just realised if you change the regex to scan for http://.* then it\u0026rsquo;ll select all website URLs.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eruby -e \u003cspan class=\"s1\"\u003e\u0026#39;a = ARGF.read.scan(/^http://.*$/); a.each { |url| `open #{url}` }\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"Mac Tips you may not know","date_published":"2009-08-31T13:08:48Z","date_modified":"2009-08-31T13:08:48Z","id":"https://caiustheory.com/mac-tips-you-may-not-know/","url":"https://caiustheory.com/mac-tips-you-may-not-know/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eHere are some mac tips I know and consider \u0026ldquo;basic\u0026rdquo; mac knowledge, but no-one else seems to know.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eexposé\u003c/strong\u003e key is on modern mac keyboards, looks like a load of squares on the F3 key.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌘ + Exposé key\u003c/strong\u003e =\u0026gt; Show Desktop\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌃ + Exposé key\u003c/strong\u003e =\u0026gt; Show Application Windows\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌥ + Brightness keys\u003c/strong\u003e =\u0026gt; Open Displays prefpane in System Preferences.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌥ + Exposé key\u003c/strong\u003e =\u0026gt; Open Exposé \u0026amp; Spaces prefpane in System Preferences.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌥ + Dashboard key\u003c/strong\u003e =\u0026gt; Open Exposé \u0026amp; Spaces prefpane in System Preferences.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌥ + Keyboard Backlight keys\u003c/strong\u003e =\u0026gt; Open Keyboard prefpane in System Preferences. \u003cem\u003e(Only on laptops with keyboard backlighting.)\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌥ + Volume keys\u003c/strong\u003e =\u0026gt; Open Sound prefpane in System Preferences.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⇧ + Volume keys\u003c/strong\u003e =\u0026gt; Adjust the volume with the feedback noise setting toggled. If you normally have feedback \u0026ldquo;blips\u0026rdquo;, it\u0026rsquo;ll be silent. Or vice versa.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌥ + ⇧ + Volume keys\u003c/strong\u003e =\u0026gt; Adjust the volume in 1/4 of a usual step.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e⌃ + Eject key\u003c/strong\u003e =\u0026gt; Shows the Shut Down dialog.\u003c/li\u003e\n\u003cli\u003eIn the shut down dialog, hit \u003cem\u003eR\u003c/em\u003e to restart, \u003cem\u003eS\u003c/em\u003e to sleep, \u003cem\u003e⎋\u003c/em\u003e to cancel.\u003c/li\u003e\n\u003cli\u003e(Pretty much) Any dialog that pops up, hitting \u003cem\u003e⎋\u003c/em\u003e will push the \u0026ldquo;cancel\u0026rdquo; button.\u003c/li\u003e\n\u003cli\u003eIn \u0026ldquo;Show all windows\u0026rdquo; or \u0026ldquo;Show application windows\u0026rdquo; exposé modes, hit the tab key to cycle through applications.\u003c/li\u003e\n\u003cli\u003eHit the space bar in exposé to activate \u0026ldquo;Quick Look\u0026rdquo; + windows pop up to 100% size as you mouse over them.\u003c/li\u003e\n\u003cli\u003eHold down \u003cstrong\u003e⌃ + ⇧\u003c/strong\u003e when mousing over the dock to toggle magnification whilst the keys are down.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cem\u003e(Alternative title \u003ca href=\"http://www.petercooper.co.uk/\"\u003ePeter Cooper\u003c/a\u003e suggested, \u0026ldquo;A miscellany of input device co-ordinations to modulate the response of Apple\u0026rsquo;s task preview and switching subsystem\u0026rdquo;)\u003c/em\u003e\u003c/p\u003e\n"},{"title":"Education Network Restrictions","date_published":"2009-07-31T14:14:16Z","date_modified":"2009-07-31T14:14:16Z","id":"https://caiustheory.com/education-network-restrictions/","url":"https://caiustheory.com/education-network-restrictions/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cem\u003eThis is a re-run of an old post I took offline in an old server move and hadn\u0026rsquo;t re-published.\u003c/em\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eHaving been on two college systems and various university networks, I\u0026rsquo;m just amazed at the levels of freedom you have on some, and how locked down others are.\u003c/p\u003e\n\u003cp\u003eTake the first university network I ever used for example.  It was pretty much totally open, to the point that I could game quite freely, and the administrator only picked me up because I was logged in as admin and not a normal user.  (I didn\u0026rsquo;t have an account for that machine.)\u003c/p\u003e\n\u003cp\u003eGoing from that to my school network was a very big shock as it was moderately filtered through third party filtering software.  This meant you couldn\u0026rsquo;t go on the usual \u003ca href=\"http://en.wikipedia.org/wiki/NSFW\" title=\"Not Safe for Work\"\u003eNSFW\u003c/a\u003e stuff, but still had access to other sites that could be seen as bad, such as proxy sites, or IRC java Clients for example.\u003c/p\u003e\n\u003cp\u003eHaving moved from my old (slightly crass) college to my new one, its interesting how filtered this one is.  You can\u0026rsquo;t seem to go on a site with \u003ccode\u003eproxy\u003c/code\u003e or \u003ccode\u003eirc\u003c/code\u003e in the URL, except \u003cem\u003eclean\u003c/em\u003e sites like the \u003ca href=\"http://www.bbc.co.uk/\" title=\"British Broadcasting Corporation\"\u003eBBC\u003c/a\u003e or \u003ca href=\"http://wikipedia.org/\" title=\"Wikipedia in English\"\u003eWikipedia\u003c/a\u003e.  The Proxy searching only came about through looking for web based IRC solutions.\u003c/p\u003e\n\u003cp\u003ePersonally I think the universities have got it right.  With all the students they have, they just limit the things they definitely have to, and allow everything else.  (Blacklisting technique.)  Both colleges seem to do the opposite - block everything until its verified and unblocked.  (Whitelisting technique.)\u003c/p\u003e\n\u003cp\u003eThe way I see it, the problem with the white listing technique is that people will always find a way around whatever restrictions are in place.  For instance, I\u0026rsquo;m locked out of all of my web based email sites, so I can\u0026rsquo;t email anyone.  Its not the not being able to send that bothers me, its the not being able to save text that I\u0026rsquo;ve written in college to a website to then retrieve it from home that annoys me.\u003c/p\u003e\n\u003cp\u003eSo how did I work around this restriction?  Well I remembered that Google had bought \u003ca href=\"http://writely.com/\" title=\"Collaborative Writing on the web\"\u003eWritely\u003c/a\u003e at some point recently, so one quick sign in later and I\u0026rsquo;ve got my own little area where I can save, organise and edit text based files.  All I have to do when I get home is login, copy / paste into my email client and hit send.\u003c/p\u003e\n\u003cp\u003eOne word that isn\u0026rsquo;t blocked yet is \u003ccode\u003eblog\u003c/code\u003e, so I can still post this, and edit my posts.  However, I\u0026rsquo;m still writing it in \u003ca href=\"http://writely.com/\" title=\"Collaborative Writing on the web\"\u003eWritely\u003c/a\u003e and checking my \u003ca href=\"http://daringfireball.net/projects/markdown/\" title=\"Markup HTML without HTML\"\u003emarkdown\u003c/a\u003e syntax is correct with \u003ca href=\"http://daringfireball.net/projects/markdown/dingus/\" title=\"Preview and Practice MarkDown \u0026amp; SmartyPants\"\u003eDingus\u003c/a\u003e.  The writely interface is just that much nicer than notepad.\u003c/p\u003e\n"},{"title":"Capitalise \"ringer\" on the iPhone Volume Bezel","date_published":"2009-06-20T15:44:04Z","date_modified":"2009-06-20T15:44:04Z","id":"https://caiustheory.com/capitalise-ringer-on-the-iphone-volume-bezel/","url":"https://caiustheory.com/capitalise-ringer-on-the-iphone-volume-bezel/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cstrong\u003eBackstory:\u003c/strong\u003e Got myself a first generation iPhone second hand and unlocked it to work on my existing T-Mobile (Official iPhone network in the UK is O2.) Noticed after a week or so of owning it that when you change the volume on the phone, the bezel that comes up says \u0026ldquo;ringer\u0026rdquo; across the top. But when you have headphones plugged in, it says \u0026ldquo;Headphones\u0026rdquo;. \u003cem\u003e(Note the capitalisation difference.)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eNow I\u0026rsquo;m not usually bothered by stuff like this (honest!) but as soon as I\u0026rsquo;d noticed the \u003cem\u003e\u0026ldquo;bug\u0026rdquo;\u003c/em\u003e, I couldn\u0026rsquo;t help but think of it everytime I changed the volume, whether I was looking at the screen or not. Seeing as I\u0026rsquo;m running a jailbroken phone, and therefore have SSH access to it, I figured the string would be defined in a .strings file somewhere in the \u003ccode\u003e/System\u003c/code\u003e folder. And I\u0026rsquo;d be able to change it!\u003c/p\u003e\n\u003cp\u003eFast-forward a few months and I install the iPhone OS 3.0 update (jailbroken of course), and finally decide to turn the phone\u0026rsquo;s SSH server on and go looking for the setting. To do so I figured I\u0026rsquo;d just need \u003ccode\u003egrep\u003c/code\u003e installed on the phone - I could copy the file itself to my mac and edit it there.\u003c/p\u003e\n\u003cp\u003eSo I connect to the phone, have a poke around the filesystem and then start a search to find the correct file:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e# On the iPhone\n$ cd /System/Library/\n$ grep -r \u0026quot;ringer\u0026quot; *\nBinary file CoreServices/SpringBoard.app/English.lproj/SpringBoard.strings matches\nBinary file CoreServices/SpringBoard.app/M68AP.plist matches\nBinary file CoreServices/SpringBoard.app/SpringBoard matches\nBinary file Frameworks/CFNetwork.framework/CFNetwork matches\nBinary file Frameworks/CFNetwork.framework/da.lproj/Localizable.strings matches\nBinary file Frameworks/CFNetwork.framework/no.lproj/Localizable.strings matches\nBinary file Frameworks/Foundation.framework/da.lproj/URL.strings matches\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAt which point I stopped the grep search (\u003ccode\u003e^C\u003c/code\u003e) because I know the home screen of the iPhone is the SpringBoard.app, so I figured it would be in the file \u003ccode\u003eSpringBoard.app/English.lproj/SpringBoard.strings\u003c/code\u003e. Making sure to have SSH enabled on your mac, a simple \u003ccode\u003escp CoreServices/SpringBoard.app/English.lproj/SpringBoard.strings user@your_mac.local:\u003c/code\u003e later and the file is sat in my home folder on my mac.\u003c/p\u003e\n\u003cp\u003eSwitching to the mac, now I try and open the file with TextMate, only to realise its in binary format. I need it in the nice XML format to edit it, so a quick google later and I\u0026rsquo;ve found a hint on \u003ca href=\"http://macosxhints.com\"\u003eMacOSXHints\u003c/a\u003e telling me how to convert from \u003ca href=\"http://www.macosxhints.com/article.php?story=20050430105126392\"\u003ebinary to xml plist format\u003c/a\u003e.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e# On the mac\n$ plutil -convert xml1 SpringBoard.strings\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen opening the file in TextMate was a bit more successful! I can actually understand what its defining now. Search through the file for \u0026ldquo;ringer\u0026rdquo; and I found the following lines:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;key\u0026gt;\u003c/span\u003eRINGER_VOLUME\u003cspan class=\"nt\"\u003e\u0026lt;/key\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;string\u0026gt;\u003c/span\u003eringer\u003cspan class=\"nt\"\u003e\u0026lt;/string\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eChange the \u0026ldquo;ringer\u0026rdquo; to \u0026ldquo;Ringer\u0026rdquo; between the \u003ccode\u003e\u0026lt;string\u0026gt;\u003c/code\u003e and my editing work is complete! Yes, it really is that easy to edit an interface string that is defined in a \u003ccode\u003e.string\u003c/code\u003e. Now I just need to convert the file back to binary, and copy it back to the phone. Converting back to binary file is one line, just change the \u003ccode\u003exml1\u003c/code\u003e in the previous command to \u003ccode\u003ebinary1\u003c/code\u003e.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e# On the mac\n$ plutil -convert binary1 SpringBoard.strings\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd then scp it back to the phone, make a backup of the existing file, and overwrite the existing file with the new one I\u0026rsquo;ve edited:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e# On the iPhone\n$ cd ~\n$ scp user@mac_name.local:SpringBoard.strings .\n$ cd /System/Library/CoreServices/SpringBoard.app/English.lproj/\n$ mv SpringBoard.strings SpringBoard.strings.bak\n$ cp ~/SpringBoard.strings SpringBoard.strings\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd then restart the phone, either in the usual manner or just run \u003ccode\u003ereboot\u003c/code\u003e on the phone via SSH. Lo and behold once its rebooted and I changed the volume, it read \u0026ldquo;Ringer\u0026rdquo;!\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://caius.name/images/ringer.jpg\" alt=\"Screenshot of Volume bezel\"\u003e\u003c/p\u003e\n"},{"title":"Adding XHTML output validation to Cucumber stories","date_published":"2009-06-16T10:19:11Z","date_modified":"2009-06-16T10:19:11Z","id":"https://caiustheory.com/adding-xhtml-output-validation-to-cucumber-stories/","url":"https://caiustheory.com/adding-xhtml-output-validation-to-cucumber-stories/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eAt the 2009 \u003ca href=\"http://barcampleeds.com/\"\u003eBarcamp Leeds\u003c/a\u003e I attended a talk by \u003ca href=\"http://neilcrosby.com/vcard/\"\u003eNeil Crosby\u003c/a\u003e where he talked about automated testing, and about how he felt there was a gap in everything that people were testing. Everyone has unit tests, and people are doing full stack testing too, but no-one (so he feels) does XHTML/CSS/JS validation as part of their automated test suite. And certainly from what I\u0026rsquo;ve seen on the mainstream Ruby site\u0026rsquo;s about testing, I agreed with him.\u003c/p\u003e\n\u003cp\u003eSo after his talk I had a quick look at his \u003ca href=\"http://github.com/NeilCrosby/frontend-test-suite/tree/master\"\u003efrontend test suite\u003c/a\u003e, and started wondering where exactly I would fit frontend validation testing into my workflow. Would it be part of my unit tests (RSpec), or part of the full stack tests (Cucumber)? As you\u0026rsquo;ve probably guessed by the title of this post, its ended up going into my cucumber tests. Since the initial play its been something I\u0026rsquo;ve mused about occasionally, but not something I\u0026rsquo;ve actively looked into how to implement as part of my test workflow.\u003c/p\u003e\n\u003cp\u003eFast-forward a few weeks from \u003ca href=\"http://barcampleeds.com/\"\u003eBarcamp Leeds\u003c/a\u003e and I see a news article in my feed reader entitled \u003ca href=\"http://tenderlovemaking.com/2009/06/12/easy-markup-validation/\"\u003e\u0026ldquo;Easy Markup Validation\u0026rdquo;\u003c/a\u003e which gets me hopeful someone\u0026rsquo;s solved this frontend validation thing easily for Rubyists. A quick read through and I\u0026rsquo;m sold on it and installing the gem. Opened an existing project I\u0026rsquo;m working on which has a fairly extensive test suite (both unit tests \u0026amp; full stack tests) and tried to slot the validation into my controller unit tests.\u003c/p\u003e\n\u003cp\u003eProblem with doing this is by default RSpec-rails doesn\u0026rsquo;t generate the views in your controller specs. At that point I realised I was already generating the full page when I was doing a full stack test using \u003ca href=\"http://github.com/langalex/culerity/tree/master\"\u003eculerity\u003c/a\u003e and \u003ca href=\"http://cukes.info/\"\u003ecucumber\u003c/a\u003e. So why not just add a cucumber step in my stories to validate the HTML on each page I visit? Mainly because its not enough of a failure for this app to have invalid XHTML markup. Having valid markup would be nice, but I\u0026rsquo;d rather have it as a separate test to my stories in some way.\u003c/p\u003e\n\u003cp\u003eCurrently I just do that by only validating if \u003ccode\u003eENV[\u0026quot;VALIDATION\u0026quot;]\u003c/code\u003e is set to anything, so a normal run of my cucumber stories will just test the app does what its supposed to do. If I run them with \u003ccode\u003eVALIDATION=true\u003c/code\u003e then it will check my markup is valid as well.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003efeatures/support/env.rb\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;markup_validity\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"no\"\u003eENV\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;VALIDATION\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003efeatures/step_definitions/general_steps.rb\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eThen\u003c/span\u003e \u003cspan class=\"s\"\u003e%r/the page is valid XHTML/\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vg\"\u003e$browser\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ehtml\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshould\u003c/span\u003e \u003cspan class=\"n\"\u003ebe_xhtml_strict\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"no\"\u003eENV\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;VALIDATION\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003efeatures/logging_in.feature\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-cucumber\" data-lang=\"cucumber\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eFeature:\u003c/span\u003e\u003cspan class=\"nf\"\u003e Logging in\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e  In order to do stuff\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e  As a registered user\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e  I want to login\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e  \u003c/span\u003e\u003cspan class=\"k\"\u003eScenario:\u003c/span\u003e\u003cspan class=\"nf\"\u003e Successful Login\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e\u003c/span\u003e\u003cspan class=\"k\"\u003e    Given \u003c/span\u003e\u003cspan class=\"nf\"\u003ethere is a user called \u0026#34;\u003c/span\u003e\u003cspan class=\"s\"\u003eCaius\u003c/span\u003e\u003cspan class=\"nf\"\u003e\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eWhen \u003c/span\u003e\u003cspan class=\"nf\"\u003eI goto the homepage\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eThen \u003c/span\u003e\u003cspan class=\"nf\"\u003ethe page is valid XHTML\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eWhen \u003c/span\u003e\u003cspan class=\"nf\"\u003eI click on the \u0026#34;\u003c/span\u003e\u003cspan class=\"s\"\u003eLogin\u003c/span\u003e\u003cspan class=\"nf\"\u003e\u0026#34; link\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eThen \u003c/span\u003e\u003cspan class=\"nf\"\u003eI am redirected to the login page\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eAnd \u003c/span\u003e\u003cspan class=\"nf\"\u003ethe page is valid XHTML\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eWhen \u003c/span\u003e\u003cspan class=\"nf\"\u003eI enter my login details\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eAnd \u003c/span\u003e\u003cspan class=\"nf\"\u003eI click \u0026#34;\u003c/span\u003e\u003cspan class=\"s\"\u003eLogin\u003c/span\u003e\u003cspan class=\"nf\"\u003e\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eThen \u003c/span\u003e\u003cspan class=\"nf\"\u003eI am redirected to my dashboard\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eAnd \u003c/span\u003e\u003cspan class=\"nf\"\u003ethe page is valid XHTML\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow when I run \u003ccode\u003ecucumber features/logging_in.feature\u003c/code\u003e, it doesn\u0026rsquo;t validate the HTML, it just makes sure that I can login as my user and that I am redirected to the right places. But if I run \u003ccode\u003eVALIDATION=true cucumber features/logging_in.feature\u003c/code\u003e, then it \u003cem\u003edoes\u003c/em\u003e validate my XHTML on the homepage, the login page and on the user\u0026rsquo;s dashboard. If it fails validation then it gives you a fairly helpful error message as to what it was expecting and what it found instead.\u003c/p\u003e\n\u003cp\u003eFrom a quick run against a couple of stories in my app I discovered that I\u0026rsquo;ve not been wrapping form elements in an enclosing element, so they\u0026rsquo;ve been quickly fixed and now they validate. Now I realise this gem is only testing XHTML output, and doesn\u0026rsquo;t include CSS or JS validation, but from a quick peek at the gem\u0026rsquo;s source it should be fairly easy to add both of those in I think, although again they aren\u0026rsquo;t major errors for me yet in this app.\u003c/p\u003e\n"},{"title":"Quantum Javascript Bug","date_published":"2009-06-04T15:12:24Z","date_modified":"2009-06-04T15:12:24Z","id":"https://caiustheory.com/quantum-javascript-bug/","url":"https://caiustheory.com/quantum-javascript-bug/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo I\u0026rsquo;ve got some js I\u0026rsquo;ve written to update a couple of \u003ccode\u003e\u0026lt;select\u0026gt;\u003c/code\u003e lists in a form, and it was all working fine for me (under Safari.) \u003ca href=\"http://johnleach.co.uk/\"\u003eJohn\u003c/a\u003e happened to mention it wasn\u0026rsquo;t working for him under Firefox, so I fired up Firefox and took a look. Could reproduce it perfectly, changing the first popup was populating the second one, but then wasn\u0026rsquo;t selecting the right value from the list.\u003c/p\u003e\n\u003cp\u003eHaving no idea what was happened I figured I\u0026rsquo;d enable firebug and watch it execute to figure out what was happening. Enabled firebug, reloaded the page, selected from the first popup… and \u003cstrong\u003evoila!\u003c/strong\u003e It updated the second one and selected the correct row! WTF!!!\u003c/p\u003e\n\u003cp\u003eTurned firebug off and it didn\u0026rsquo;t work, turned it back on and it worked. Figured it might be something buggy in the Firefox 3.0.5 js runtime, so I grabbed a copy of the new \u003ca href=\"http://www.mozilla.com/en-US/firefox/all-beta.html\"\u003ebeta 3.5\u003c/a\u003e and tried it in there—still failed to update the page as it should.\u003c/p\u003e\n\u003cp\u003eThen started poking around the javascript code, the function that was seemingly failing to run was being triggered by a setTimeout() call set to 1 second. We figured it might be the timing causing it, so started playing around with the time, tried anything from ½ a second up to 4 seconds but still no joy in firefox with firebug turned off.\u003c/p\u003e\n\u003cp\u003eThen \u003ca href=\"http://johnleach.co.uk/\"\u003eJohn\u003c/a\u003e went looking for the javascript errors in firefox (with firebug off) and discovered that it was throwing an error because \u003ccode\u003ewindow.console\u003c/code\u003e didn\u0026rsquo;t exist. All of a sudden it made perfect sense! Safari has \u003ccode\u003ewindow.console.log()\u003c/code\u003e for writing to the console log, as does firebug. But of course firefox \u003cem\u003ewithout\u003c/em\u003e firebug doesn\u0026rsquo;t!\u003c/p\u003e\n\u003cp\u003eSo the function was just exiting on that error. It was very weird initially to have it work perfectly as soon as the developer tools were enabled!\u003c/p\u003e\n"},{"title":"Automatically Deploying Website From Remote Git Repository","date_published":"2009-05-30T02:30:40Z","date_modified":"2009-05-30T02:30:40Z","id":"https://caiustheory.com/automatically-deploying-website-from-remote-git-repository/","url":"https://caiustheory.com/automatically-deploying-website-from-remote-git-repository/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eBefore I start, I\u0026rsquo;ll just quickly run through where I put stuff on my server. Apache logs and config are in the ubuntu default folders: \u003ccode\u003e/var/log/apache2\u003c/code\u003e and \u003ccode\u003e/etc/apache2/\u003c/code\u003e respectively.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eWebsites:  /home/caius/vhosts/\u0026lt;domain name\u0026gt;/htdocs\nGit Repos: /home/caius/git/\u0026lt;domain name\u0026gt;.git\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eSo I have a git repo locally, \u003ccode\u003e~/projects/somesite.com/\u003c/code\u003e, and want to deploy it to my webserver. I\u0026rsquo;ll keep the git repo in \u003ccode\u003e~/git/\u003c/code\u003e and set it up so that when I push to the repo \u003cem\u003e(over ssh)\u003c/em\u003e it will automatically checkout the new changes into the website\u0026rsquo;s htdocs folder.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m assuming DNS is already setup (or I\u0026rsquo;ve used \u003ca href=\"http://github.com/bjeanes/ghost/tree/master\"\u003eghost\u003c/a\u003e to map it locally.) And that I\u0026rsquo;ve setup the virtualhost in apache pointing at \u003ccode\u003e/home/caius/vhosts/somesite.com/htdocs\u003c/code\u003e and reloaded apache so the config is in place.\u003c/p\u003e\n\u003ch2 id=\"remote-machine\"\u003eRemote Machine\u003c/h2\u003e\n\u003cp\u003eWe create a bare git repo, then point the working tree at the docroot of our website. This means all the git stuff is kept in the \u003ccode\u003esomesite.git\u003c/code\u003e folder, but the files themselves are checked out to the website\u0026rsquo;s folder. Then we setup a post-receive hook to update the worktree folder after new changes have been pushed to the repo.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ cd git\n$ mkdir somesite.git\n$ cd somesite.git/\n$ git init --bare\nInitialized empty Git repository in /home/caius/git/somesite.git/\n$ git --bare update-server-info\n$ git config core.worktree /home/caius/vhosts/somesite.com/htdocs\n$ git config core.bare false\n$ git config receive.denycurrentbranch ignore\n$ cat \u0026gt; hooks/post-receive\n#!/bin/sh\ngit checkout -f\n^D\n$ chmod +x hooks/post-receive\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"local-machine\"\u003eLocal Machine\u003c/h2\u003e\n\u003cp\u003eAnd now on the client machine we add the remote repo as a git remote, and then push to it.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ git remote add web ssh://myserver/home/caius/git/somesite.git\n$ git push web +master:refs/heads/master\nCounting objects: 3, done.\nWriting objects: 100% (3/3), 229 bytes, done.\nTotal 3 (delta 0), reused 0 (delta 0)\nTo ssh://myserver/home/caius/git/somesite.git\n * [new branch]      master -\u0026gt; master\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"all-done\"\u003eAll Done\u003c/h2\u003e\n\u003cp\u003eAnd now if you go to \u003cem\u003esomesite.com\u003c/em\u003e you\u0026rsquo;ll see the contents of your git repo there. (\u003cem\u003esomesite.com\u003c/em\u003e is just an example url though, I don\u0026rsquo;t actually own it!)\u003c/p\u003e\n\u003ch3 id=\"helpful-urls\"\u003eHelpful URLs\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://toroid.org/ams/git-website-howto\"\u003ehttp://toroid.org/ams/git-website-howto\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.mblondel.org/journal/2008/05/25/git-memo/\"\u003ehttp://www.mblondel.org/journal/2008/05/25/git-memo/\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://tatey.com/2009/04/29/jekyll-meets-dreamhost-automated-deployment-for-jekyll-with-git.html\"\u003ehttp://tatey.com/2009/04/29/jekyll-meets-dreamhost-automated-deployment-for-jekyll-with-git.html\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n"},{"title":"Find shell commands with which","date_published":"2009-04-19T15:02:04Z","date_modified":"2009-04-19T15:02:04Z","id":"https://caiustheory.com/find-shell-commands-with-which/","url":"https://caiustheory.com/find-shell-commands-with-which/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo I have this command in my $PATH, \u003ccode\u003eapachectl\u003c/code\u003e. Because I\u0026rsquo;m on a mac and I\u0026rsquo;ve installed apache2 through \u003ca href=\"http://macports.org/\"\u003eMacPorts\u003c/a\u003e, the command that gets found first is my macports install in \u003ccode\u003e/opt\u003c/code\u003e. Up until now I\u0026rsquo;ve always known that \u003ccode\u003ewhich apachectl\u003c/code\u003e will find that location, but to find any other locations of \u003ccode\u003eapachectl\u003c/code\u003e I\u0026rsquo;d usually use \u003ccode\u003elocate\u003c/code\u003e and \u003ccode\u003eegrep\u003c/code\u003e together.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s my original workflow, lets find the location of the \u003ccode\u003eapachectl\u003c/code\u003e being called when I don\u0026rsquo;t specify a path.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eJulius:~ caius$ which apachectl\n/opt/local/apache2/bin/apachectl\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eSimple enough. Now lets figure out what other locations there\u0026rsquo;s an \u003ccode\u003eapachectl\u003c/code\u003e installed at.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eJulius:~ caius$ locate apachectl | egrep \u0026quot;\\/apachectl$\u0026quot;\n/opt/local/apache2/bin/apachectl\n/opt/local/var/macports/software/apache2/2.2.11_0+darwin_9/opt/local/apache2/bin/apachectl\n/usr/sbin/apachectl\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eRight, so now I know where else a command exists in the filesystem called \u003ccode\u003eapachectl\u003c/code\u003e, but I don\u0026rsquo;t know if any of those is in my $PATH, or what order they come in when searching through my $PATH. In this (old) workflow I\u0026rsquo;d have compared them to my $PATH manually as there\u0026rsquo;s so few of them.\u003c/p\u003e\n\u003cp\u003eSo I noticed \u003ca href=\"http://awhitebox.com\"\u003eAli\u003c/a\u003e googling for the \u003ccode\u003ewhich\u003c/code\u003e man page on IRC, and \u003cem\u003e(quite stupidly)\u003c/em\u003e poked fun at him for doing so. I then swallowed my ego and actually followed the link to the man page, and boy was I glad I did. Just shows with even a fairly simple command like \u003ccode\u003ewhich\u003c/code\u003e, you sure don\u0026rsquo;t know everything!\u003c/p\u003e\n\u003cp\u003eWhat I discovered was that \u003ccode\u003ewhich\u003c/code\u003e has a single flag you can pass it, \u003ccode\u003e-a\u003c/code\u003e. From the \u003ca href=\"https://www.freebsd.org/cgi/man.cgi?query=which\u0026amp;format=html\"\u003eman page\u003c/a\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e-a     print all matching pathnames of each argument\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eRight. So that \u003ccode\u003elocate | grep\u003c/code\u003e command plus manually figuring out what is in my $PATH is really hard work then. \u003ccode\u003ewhich -a\u003c/code\u003e should give us the same results, but a lot faster and with a lot less manual thought.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eJulius:~ caius$ which -a apachectl\n/opt/local/apache2/bin/apachectl\n/usr/sbin/apachectl\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd hey presto, yet another useful bit of bash knowledge for me, thanks to \u003ca href=\"http://awhitebox.com\"\u003eAli\u003c/a\u003e not being afraid to \u003c!-- raw HTML omitted --\u003eRTFM\u003c!-- raw HTML omitted --\u003e!\u003c/p\u003e\n"},{"title":"Validating Data with Regular Expressions in Ruby","date_published":"2009-04-11T12:41:48Z","date_modified":"2009-04-11T12:41:48Z","id":"https://caiustheory.com/validating-data-with-regular-expressions-in-ruby/","url":"https://caiustheory.com/validating-data-with-regular-expressions-in-ruby/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI happened to be sent a link to the \u003ca href=\"https://www.owasp.org/index.php/Main_Page\"\u003eOWASP\u003c/a\u003e paper on \u003ca href=\"https://www.owasp.org/images/8/89/Rails_Security_2.pdf\"\u003eRails Security\u003c/a\u003e recently and started reading it. Partway in there\u0026rsquo;s a section on Regular Expressions, which opens with the following line:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eA common pitfall in Ruby\u0026rsquo;s regular expressions is to match the string\u0026rsquo;s beginning and end by \u003ccode\u003e^\u003c/code\u003e and \u003ccode\u003e$\u003c/code\u003e, instead of \u003ccode\u003e\\A\u003c/code\u003e and \u003ccode\u003e\\z\u003c/code\u003e.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eNow I\u0026rsquo;ve never used \u003ccode\u003e\\A\u003c/code\u003e and \u003ccode\u003e\\z\u003c/code\u003e in my regular expressions to validate data, I\u0026rsquo;ve only ever used \u003ccode\u003e^\u003c/code\u003e and \u003ccode\u003e$\u003c/code\u003e assuming they matched the start and end of the string. This becomes an issue with validating data in rails, because \u003ccode\u003e%0A\u003c/code\u003e (\u003ccode\u003e\\n\u003c/code\u003e URL encoded) is decoded by rails before passing the string to your model to validate.\u003c/p\u003e\n\u003ch2 id=\"testing-our-expectations\"\u003eTesting our expectations\u003c/h2\u003e\n\u003cp\u003eLets say we want to validate the string as a username for our app. A username is 5 characters long and consists only of lowercase letters.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eregex\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e/^[a-z]{5}$/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFirst we make sure it matches the data we want it to:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;caius\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eregex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; true\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExcellent, that validated. Now we\u0026rsquo;ll try a shorter string, which we expect to fail.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;cai\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eregex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; false\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce more, it behaves how we expected it to. The shorter string was rejected as we wanted it to be. Now, what happens if we test a string with a newline character in it? We\u0026rsquo;ll make sure the data before the \u003ccode\u003e\\n\u003c/code\u003e is valid, and then add some more data after the newline.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;caius\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003efoo\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eregex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; true\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUh oh! That validated and would\u0026rsquo;ve been saved as a username?!\u003c/p\u003e\n\u003cp\u003eLets have a look at exactly what\u0026rsquo;s happening there, the \u003ccode\u003e$\u003c/code\u003e matches the \u003ccode\u003e\\n\u003c/code\u003e character, so the regex is only matching the first 5 characters of the string, and just ignores anything after the \u003ccode\u003e\\n\u003c/code\u003e. As it turns out, this is exactly what we\u0026rsquo;ve asked the regex to match, but we didn\u0026rsquo;t want this behaviour.\u003c/p\u003e\n\u003cp\u003eNow you might be thinking, \u0026ldquo;So what? someone can have a username with a newline in it.\u0026rdquo; For starters this will probably display weirdly anywhere you use their username, but more importantly it opens your application to an injection attack. Suppose they took advantage of this by setting their username to include some javascript on the page which stole your login cookie and sent it to them. You view their account in the admin section and oh no! They can login as your admin account and do what they want.\u003c/p\u003e\n\u003cp\u003eSimple example of this is just having it output an alert dialog. \u003cem\u003e(This is actually the code I\u0026rsquo;ll use to test an application as its not malicious, but blindingly obvious if the javascript is executed or not.)\u003c/em\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;caius\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026lt;script\u0026gt;alert(\u0026#39;hello\u0026#39;)\u0026lt;/script\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eregex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; true\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOk, so that was the result we were expecting this time, although it\u0026rsquo;s still not the outcome we wanted. Anytime their username is viewed (providing you aren\u0026rsquo;t escaping the data to HTML entities) you\u0026rsquo;ll see the following:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://caius.name/images/qs/javascript-alert-dialog.png\" alt=\"javascript alert dialog\"\u003e\u003c/p\u003e\n\u003ch2 id=\"the-solution\"\u003eThe Solution\u003c/h2\u003e\n\u003cp\u003eHaving realised from our testing above that ^$ matches the beginning/end of a \u003cem\u003eline\u003c/em\u003e in ruby not the beginning and end of a \u003cem\u003estring\u003c/em\u003e, I hear you cry, \u0026ldquo;How do we make sure we\u0026rsquo;re matching the entire string?!\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThe answer is pretty simple. Just swap out \u003ccode\u003e^$\u003c/code\u003e for \u003ccode\u003e\\A\\z\u003c/code\u003e. Lets go ahead and try this with the same data as we have above, but with the modified regular expression.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003enew_regex\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e/\\A[a-z]{5}\\z/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;caius\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enew_regex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; true\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat\u0026rsquo;s a good start, the valid string still matches.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;cai\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enew_regex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; false\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLooks like it\u0026rsquo;s going well, invalid string is invalid.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;caius\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003efoo\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enew_regex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; false\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOh Excellent! It\u0026rsquo;s validating this one correctly now.\u003c/p\u003e\n\u003cp\u003eAnd just for consistency, lets test it with a more likely attack string.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;caius\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026lt;script\u0026gt;alert(\u0026#39;hello\u0026#39;)\u0026lt;/script\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enew_regex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; false\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFantastic! We\u0026rsquo;ve fixed the security hole in our validation of the user\u0026rsquo;s username.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eIf you want to actually run the code above you\u0026rsquo;ll need the following at the start of the ruby script to patch the validate method into String.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eString\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003evalidate\u003c/span\u003e \u003cspan class=\"n\"\u003eregex\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eregex\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003enil?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003e\u003cstrong\u003eUpdate:\u003c/strong\u003e\u003c/em\u003e I had \u003ccode\u003e\\Z\u003c/code\u003e in the \u003ccode\u003enew_regex\u003c/code\u003e rather than the \u003ccode\u003e\\z\u003c/code\u003e it should\u0026rsquo;ve been. Thanks \u003ca href=\"http://ciaranwal.sh/\"\u003eCiarán\u003c/a\u003e.\u003c/p\u003e\n"},{"title":"Safari 4 Hidden Preferences","date_published":"2009-02-24T16:11:05Z","date_modified":"2009-02-24T16:11:05Z","id":"https://caiustheory.com/safari-4-hidden-preferences/","url":"https://caiustheory.com/safari-4-hidden-preferences/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003cstrong\u003eUpdated 2009-06-09:\u003c/strong\u003e This post is for the Safari 4 \u003cstrong\u003ebeta\u003c/strong\u003e and will not work with the new Safari 4 released yesterday at the WWDC keynote. I\u0026rsquo;ve had a look through that release and can\u0026rsquo;t see any way to revert the address bar, etc sorry.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eHaving a quick poke through the new Safari binary yields the following strings:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ strings /Applications/Safari.app/Contents/MacOS/Safari | grep DebugSafari4\nDebugSafari4TabBarIsOnTop\nDebugSafari4IncludeToolbarRedesign\nDebugSafari4IncludeFancyURLCompletionList\nDebugSafari4IncludeGoogleSuggest\nDebugSafari4LoadProgressStyle\nDebugSafari4IncludeFlowViewInBookmarksView\nDebugSafari4TopSitesZoomToPageAnimationDimsSnapshot\nDebugSafari4IncludeTopSites\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cem\u003eNB: Run these commands in Terminal.app and then you need to restart Safari for them to take effect.\u003c/em\u003e\u003c/p\u003e\n\u003ch3 id=\"debugsafari4tabbarisontop\"\u003eDebugSafari4TabBarIsOnTop\u003c/h3\u003e\n\u003cp\u003eThis moves the tab bar back where you expect it to be:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ defaults write com.apple.Safari DebugSafari4TabBarIsOnTop -bool NO\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"debugsafari4includetoolbarredesign-and-debugsafari4loadprogressstyle\"\u003eDebugSafari4IncludeToolbarRedesign and DebugSafari4LoadProgressStyle\u003c/h3\u003e\n\u003cp\u003eWhen both set to NO it restores the blue loading bar behind the URL. \u003cem\u003eAlso puts a page loading spinner in the tab itself, which looks odd with the new tabs.\u003c/em\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ defaults write com.apple.Safari DebugSafari4IncludeToolbarRedesign -bool NO\n$ defaults write com.apple.Safari DebugSafari4LoadProgressStyle -bool NO\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"debugsafari4includefancyurlcompletionlist\"\u003eDebugSafari4IncludeFancyURLCompletionList\u003c/h3\u003e\n\u003cp\u003eSwitches off the new URL autocomplete menu and goes back to the original one.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ defaults write com.apple.Safari DebugSafari4IncludeFancyURLCompletionList -bool NO\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"debugsafari4includegooglesuggest\"\u003eDebugSafari4IncludeGoogleSuggest\u003c/h3\u003e\n\u003cp\u003eTurns off the new Google suggest menu.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ defaults write com.apple.Safari DebugSafari4IncludeGoogleSuggest -bool NO\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"debugsafari4includeflowviewinbookmarksview\"\u003eDebugSafari4IncludeFlowViewInBookmarksView\u003c/h3\u003e\n\u003cp\u003eRemoves CoverFlow from the Bookmarks view entirely. (\u003ca href=\"http://twitter.com/iacas/status/1245800183\"\u003eCredit\u003c/a\u003e to \u003ca href=\"http://nslog.com/\"\u003eErik\u003c/a\u003e)\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ defaults write com.apple.Safari DebugSafari4IncludeFlowViewInBookmarksView -bool NO\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"debugsafari4topsiteszoomtopageanimationdimssnapshot\"\u003eDebugSafari4TopSitesZoomToPageAnimationDimsSnapshot\u003c/h3\u003e\n\u003cp\u003eDisables the dimming when you click on a Top Site and it scales the screenshot up to fill the screen.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ defaults write com.apple.Safari DebugSafari4TopSitesZoomToPageAnimationDimsSnapshot -bool NO\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"debugsafari4includetopsites\"\u003eDebugSafari4IncludeTopSites\u003c/h3\u003e\n\u003cp\u003eDisables Top Sites feature completely.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ defaults write com.apple.Safari DebugSafari4IncludeTopSites -bool NO\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"undoing-changes\"\u003eUndoing changes\u003c/h2\u003e\n\u003cp\u003eJust run the defaults command with the \u003ccode\u003edelete\u003c/code\u003e flag for the appropriate key you wish to delete.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ defaults delete com.apple.Safari \u0026lt;key\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cem\u003eNB: Don\u0026rsquo;t include the \u003ccode\u003e-bool NO\u003c/code\u003e at the end, it just requires the key (eg: \u0026ldquo;DebugSafari4IncludeGoogleSuggest\u0026rdquo;)\u003c/em\u003e\u003c/p\u003e\n\u003ch3 id=\"update-2009-02-26\"\u003eUpdate 2009-02-26\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"http://swedishcampground.com/safari-4-hidden-preferences#comment-3265\"\u003eJools points out in the comments\u003c/a\u003e how to reset the recent searches in the google search box.\u003c/p\u003e\n\u003ch3 id=\"update-2009-05-26\"\u003eUpdate 2009-05-26\u003c/h3\u003e\n\u003cp\u003eLowell\u0026rsquo;s kindly created a Mac OS X application to edit these settings without using Terminal. \u003ca href=\"http://github.com/cocoastep/tweaky\"\u003ehttp://github.com/cocoastep/tweaky\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"update-2010-11-18\"\u003eUpdate 2010-11-18\u003c/h3\u003e\n\u003cp\u003ePatric has kindly \u003ca href=\"http://www.movavi.com/opensource/safari-4-hidden-preferences-be\"\u003etranslated this post into Belorussian\u003c/a\u003e and posted it on his site.\u003c/p\u003e\n"},{"title":"Migrating Rubygems to Ruby 1.9.x","date_published":"2009-01-31T20:29:57Z","date_modified":"2009-01-31T20:29:57Z","id":"https://caiustheory.com/migrating-rubygems-to-ruby-19x/","url":"https://caiustheory.com/migrating-rubygems-to-ruby-19x/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo I just installed ruby 1.9.1 through \u003ca href=\"http://macports.org/\"\u003eMacPorts\u003c/a\u003e and wanted to easily migrate my rubygems across from 1.8 to see which ones would fail to install.\u003c/p\u003e\n\u003cp\u003eThought about it for a while, then came up with the following bash one-liner to do it:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egem list \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep \u003cspan class=\"s2\"\u003e\u0026#34;(\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e awk \u003cspan class=\"s1\"\u003e\u0026#39;{ print $1 }\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e xargs -L \u003cspan class=\"m\"\u003e1\u003c/span\u003e gem1.9 install\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eNB:\u003c/strong\u003e Installing Ruby 1.9.1 through macports \u003ccode\u003esudo port install ruby19\u003c/code\u003e means I get \u003ccode\u003eruby1.9\u003c/code\u003e, \u003ccode\u003egem1.9\u003c/code\u003e and \u003ccode\u003erake1.9\u003c/code\u003e installed alongside my usual 1.8 \u003ccode\u003eruby\u003c/code\u003e, \u003ccode\u003egem\u003c/code\u003e and \u003ccode\u003erake\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThat grabs the list of installed gems from \u003ccode\u003egem\u003c/code\u003e, searches for lines containing \u0026ldquo;(\u0026rdquo; so it only grabs the gem names, spits out the first section of the line, which is the name of the gem, and finally calls \u003ccode\u003egem1.9 install\u003c/code\u003e for each line via \u003ccode\u003exargs -L 1\u003c/code\u003e. Make sure to run it as root or prefix \u003ccode\u003egem1.9\u003c/code\u003e with \u003ccode\u003esudo\u003c/code\u003e. \u003cem\u003e(Or let it install in your home folder, but I hate that.)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eFrom my quick run of the above snippet, 75% of my gems installed \u003cem\u003e(73 out of 98)\u003c/em\u003e and the other few that failed to install were ones like \u003ca href=\"http://github.com/why/hpricot/tree/master\"\u003eHpricot\u003c/a\u003e that require native extensions compiling. You can see the entire list of failures and successes of the gems in \u003ca href=\"http://pastie.textmate.org/pastes/376136\"\u003ethis pastie\u003c/a\u003e\u003c/p\u003e\n"},{"title":"View Raw Source","date_published":"2009-01-31T01:54:57Z","date_modified":"2009-01-31T01:54:57Z","id":"https://caiustheory.com/view-raw-source/","url":"https://caiustheory.com/view-raw-source/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo I write this blog using \u003ca href=\"http://daringfireball.net/projects/markdown/\"\u003eMarkdown\u003c/a\u003e because I\u0026rsquo;m a human and writing \u003ccode\u003estuff \u0026lt;strong\u0026gt;with\u0026lt;/strong\u0026gt; tags\u003c/code\u003e is just \u003cstrong\u003eWRONG\u003c/strong\u003e. Thankfully, \u003ca href=\"http://daringfireball.net/\"\u003eGruber\u003c/a\u003e solved this problem by writing markdown.\u003c/p\u003e\n\u003cp\u003eNow on the \u003ca href=\"http://daringfireball.net/projects/markdown/\"\u003emarkdown page\u003c/a\u003e he says:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe best way to get a feel for Markdown’s formatting syntax is simply to look at a Markdown-formatted document. For example, you can view the Markdown source for the article text on this page here: \u003ca href=\"http://daringfireball.net/projects/markdown/index.text\"\u003ehttp://daringfireball.net/projects/markdown/index.text\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cblockquote\u003e\n\u003cp\u003e(You can use this ‘.text’ suffix trick to view the Markdown source for the content of each of the pages in this section, e.g. the Syntax and License pages.)\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eAnd ever since I noticed that I\u0026rsquo;ve always read his articles using the \u0026lsquo;.text\u0026rsquo; trick. One of the plugins I\u0026rsquo;ve been meaning to write for \u003ca href=\"http://habariproject.org/\"\u003ehabari\u003c/a\u003e is one that replicates this \u0026lsquo;.text\u0026rsquo; behaviour. So tonight I decided to try and write it, started picking through the \u003ca href=\"http://wiki.habariproject.org/en/Creating_A_Plugin\"\u003ePlugin documentation\u003c/a\u003e in preparation. Got a bit stuck with it as I\u0026rsquo;ve been out of the habari development loop for a few months, popped into \u003ca href=\"irc://irc.freenode.net/#habari\"\u003e#habari\u003c/a\u003e and asked if I was thinking along the right lines.\u003c/p\u003e\n\u003cp\u003eFew minutes later \u003ca href=\"http://asymptomatic.net/\"\u003eOwen\u003c/a\u003e pops up and sends me a link to \u003ca href=\"http://pastoid.com/bn5\"\u003eplaintext.plugin.php\u003c/a\u003e, which does exactly what I was trying to do! Couple of tweaks later (switching it to \u0026lsquo;.text\u0026rsquo; instead of \u0026lsquo;.md\u0026rsquo;) and its installed and working on this blog. Feel free to view the \u003ca href=\"http://caiustheory.com/view-raw-source.text\"\u003eraw source\u003c/a\u003e of this post. Or any other post on this site.\u003c/p\u003e\n\u003ch3 id=\"updated-2009-01-31\"\u003eUpdated 2009-01-31\u003c/h3\u003e\n\u003cp\u003eAdded to the habari-extras repo as the \u003ca href=\"http://pastoid.com/bn5\"\u003ePlaintext\u003c/a\u003e plugin.\u003c/p\u003e\n"},{"title":"This is my Compiler","date_published":"2009-01-30T12:18:56Z","date_modified":"2009-01-30T12:18:56Z","id":"https://caiustheory.com/this-is-my-compiler/","url":"https://caiustheory.com/this-is-my-compiler/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eAs some friday fun in \u003ca href=\"http://twitter.com/geekupirc\"\u003e#geekup\u003c/a\u003e we ended up converting the \u003ca href=\"http://usmilitary.about.com/od/marines/l/blriflecreed.htm\"\u003eUS Marines Creed\u003c/a\u003e to a geekier version.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThis is my compiler.\u003cbr\u003e\nThere are many like it, but this one is MINE.\u003cbr\u003e\nMy compiler is my best friend. It is my life.\u003cbr\u003e\nI must master it as I must master my life.\u003cbr\u003e\nMy compiler without me is useless. Without my compiler, I am useless.\u003cbr\u003e\nI must run my compiler true.\u003cbr\u003e\nI must run faster than my bug who is trying to kill me.\u003cbr\u003e\nI must squash him before he squashes me. I will\u0026hellip;\u003cbr\u003e\nMy compiler and myself know that what counts in war is not the warnings we squash,\u003cbr\u003e\nthe builds we create, nor the optimisations we make.\u003cbr\u003e\nWe know it is the build errors fixed that count. We will fix\u0026hellip;\u003cbr\u003e\nMy compiler is human, even as I, because it is my life.\u003cbr\u003e\nThus, I will learn it as a brother.\u003cbr\u003e\nI will learn its weaknesses, its strengths, its output, its code,\u003cbr\u003e\nits quirks, and its errors.\u003cbr\u003e\nI will ever guard it against the ravages of virii and disk failures.\u003cbr\u003e\nI will keep my compiler clean and ready, even as I am clean and ready.\u003cbr\u003e\nWe will become part of each other. We will\u0026hellip;\u003cbr\u003e\nBefore Assembler I swear this creed.\u003cbr\u003e\nMy compiler and myself are the defenders of good code.\u003cbr\u003e\nWe are the masters of our bugs.\u003cbr\u003e\nWe are the saviors of our code.\u003cbr\u003e\nSo be it, until code is compiling and there are no bugs, but compiled code.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eAnd then \u003ca href=\"http://jamx.org/\"\u003ejamx\u003c/a\u003e jumped in with the \u003ca href=\"http://en.wikipedia.org/wiki/Lord's_Prayer#English_versions\"\u003eLords Prayer\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eOur Compiler, who art in memory. GNU be thy name.\u003cbr\u003e\nThy source code come, thy will be done.\u003cbr\u003e\nOn script as it is in memory.\u003cbr\u003e\nGive us this day our daily data, and forgive us our segfaults as we forgive those who segfault against us.\u003cbr\u003e\nAnd lead us not into /dev/null but deliver us from M$,\u003cbr\u003e\nfor thine is the domain, the cpu and the peeps.\u003cbr\u003e\nfor (x=0; x\u0026lt;2; x++){ x=0; }\u003cbr\u003e\nAmen\u003c/p\u003e\n\u003c/blockquote\u003e\n"},{"title":"Install Mysql Gem on Leopard","date_published":"2009-01-21T17:09:41Z","date_modified":"2009-01-21T17:09:41Z","id":"https://caiustheory.com/install-mysql-gem-on-leopard/","url":"https://caiustheory.com/install-mysql-gem-on-leopard/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo, I keep having to reinstall mysql5 and rubygems from time to time for various reasons. I always install mysql5 through \u003ca href=\"http://macports.org/\"\u003eMacPorts\u003c/a\u003e as a dependency for the php5 port (along with various other bits for the LA*P stack).\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo port install php5 +mysql5 +pear +readline +sockets +apache2 +sqlite\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce this is installed then I have \u003ccode\u003emysql\u003c/code\u003e and can setup my databases, etc.\u003c/p\u003e\n\u003cp\u003eIgnoring the rest of the LAMP stack, I then need to connect Ruby to the Mysql I just installed through MacPorts. Its quite simple to do, once you know the right argument to pass to it. The easiest way is to just tell it where the \u003ccode\u003emysql5_conf\u003c/code\u003e file is and let it figure out the rest for itself.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo gem install mysql -- --with-mysql-config\u003cspan class=\"o\"\u003e=\u003c/span\u003e/opt/local/bin/mysql_config5\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHopefully this will save me 10 minutes of googling next time I need to do this!\u003c/p\u003e\n\u003ch3 id=\"update-2009-01-21\"\u003eUpdate 2009-01-21\u003c/h3\u003e\n\u003cp\u003eI\u0026rsquo;m an idiot and typed the \u003ccode\u003egem install\u003c/code\u003e command by hand, and ended up with \u003ccode\u003e--with-mysql-conf\u003c/code\u003e instead of \u003ccode\u003e--with-mysql-config\u003c/code\u003e. Updated now.\u003c/p\u003e\n\u003ch3 id=\"update-2009-10-19\"\u003eUpdate 2009-10-19\u003c/h3\u003e\n\u003cp\u003eOn Snow Leopard I needed to tell rubygems to install the gem as a 64-bit binary. Hattip to \u003ca href=\"http://www.schmidp.com/2009/06/14/rubyrails-and-mysql-on-snow-leopard-10a380/comment-page-1/\"\u003ePhilipp\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo env \u003cspan class=\"nv\"\u003eARCHFLAGS\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;-arch x86_64\u0026#34;\u003c/span\u003e gem install mysql -- \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  --with-mysql-config\u003cspan class=\"o\"\u003e=\u003c/span\u003e/opt/local/bin/mysql_config5\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"GTranslate","date_published":"2009-01-04T09:40:59Z","date_modified":"2009-01-04T09:40:59Z","id":"https://caiustheory.com/gtranslate/","url":"https://caiustheory.com/gtranslate/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI finally wrapped up some code I\u0026rsquo;ve been meaning to write for a while, its a wrapper for the \u003ca href=\"http://translate.google.com/\"\u003eGoogle Translate API\u003c/a\u003e. Its also the first serious time I\u0026rsquo;ve used \u003ccode\u003emethod_missing\u003c/code\u003e in a class, in this case its to add methods for translating between all the various languages.\u003c/p\u003e\n\u003cp\u003eIts fairly simple to use, there is an \u003ca href=\"http://github.com/caius/gtranslate/tree/master/examples.rb\"\u003eexamples.rb\u003c/a\u003e included with it, but the basic usage is just this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Convert from english to french\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eGoogle\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003eTranslate\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenglish_to_french\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;Bonjour\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# There is also a short(er)-hand version\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"no\"\u003eGoogle\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003eTr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003een_to_fr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAs per usual with all my code its available on my \u003ca href=\"http://github.com/caius/\"\u003egithub account\u003c/a\u003e, as the \u003ca href=\"http://github.com/caius/gtranslate/\"\u003eGTranslate\u003c/a\u003e project. I\u0026rsquo;ll throw some specs together for it and package it up as a gem soon.\u003c/p\u003e\n"},{"title":"The Shell Meme","date_published":"2008-12-30T18:29:35Z","date_modified":"2008-12-30T18:29:35Z","id":"https://caiustheory.com/the-shell-meme/","url":"https://caiustheory.com/the-shell-meme/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI ran across \u003ca href=\"http://lstoll.net/2008/04/shell-meme/\"\u003eThe Shell Meme\u003c/a\u003e on \u003ca href=\"http://lstoll.net/\"\u003eLincoln Stoll\u0026rsquo;s\u003c/a\u003e blog, and figured I\u0026rsquo;d, uh, \u003cem\u003eborrow\u003c/em\u003e it.\u003c/p\u003e\n\u003cp\u003eRun this command in a new shell:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ehistory\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e awk \u003cspan class=\"s1\"\u003e\u0026#39;{ a[$2]++ } END { for(i in a){printf \u0026#34;%5d\\t%s\\n \u0026#34;,a[i],i} }\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  sort -rn \u003cspan class=\"p\"\u003e|\u003c/span\u003e head\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI get this as the output\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e379    git\n221    cd\n181    ssh\n77    sudo\n69    ruby\n66    ls\n34    rake\n33    m\n32    bb\n31    m.\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003ebb\u003c/code\u003e changes directory straight into my \u003ca href=\"http://www.brightbox.co.uk/\"\u003eBrightBox\u003c/a\u003e source directory. \u003ccode\u003em\u003c/code\u003e and \u003ccode\u003em.\u003c/code\u003e are \u003ca href=\"http://macromates.com/\"\u003eTextMate\u003c/a\u003e alias\u0026rsquo;s to open files or directories in TextMate for editing.\u003c/p\u003e\n"},{"title":"Sending Array elements as individual arguments in Ruby","date_published":"2008-12-26T07:25:15Z","date_modified":"2008-12-26T07:25:15Z","id":"https://caiustheory.com/sending-array-elements-as-individual-arguments-in-ruby/","url":"https://caiustheory.com/sending-array-elements-as-individual-arguments-in-ruby/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eLets imagine we\u0026rsquo;ve got an array of strings, and we want to print it out as a list of strings using printf. \u003cem\u003e(If you\u0026rsquo;re complaining about my logic here, hold fire for just a second good sir/madam.)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eSo we start off with the array of strings, and then pass it to printf with the right amount of \u003ccode\u003e%s\u003c/code\u003e\u0026rsquo;s in the format string:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003earr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;one\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;two\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;three\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprintf\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;%s, %s, %s\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003earr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ~\u0026gt; -:3:in `printf\u0026#39;: too few arguments (ArgumentError)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ~\u0026gt;     from -:3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOh whoops, we\u0026rsquo;ve actually only passed \u003ccode\u003e\u0026quot;%s, %s, %s\u0026quot;, [\u0026quot;one\u0026quot;, \u0026quot;two\u0026quot;, \u0026quot;three\u0026quot;]\u003c/code\u003e to printf. So of course it whinges about not getting enough arguments. Now how do we fix this, how \u003cstrong\u003edo\u003c/strong\u003e we pass an array with each element a seperate argument to a method?\u003c/p\u003e\n\u003cp\u003eWe use the \u003ccode\u003e*\u003c/code\u003e of course! Just prefix the variable name with \u003ccode\u003e*\u003c/code\u003e and the method is passed each element as separate arguments, rather than the whole array as one arguement.\u003c/p\u003e\n\u003cp\u003eGoing back to our \u003ccode\u003eprintf\u003c/code\u003e example above, we simply insert one character \u003cem\u003e(the lowly \u003ccode\u003e*\u003c/code\u003e)\u003c/em\u003e and end up with a string being outputted.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprintf\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;%s, %s, %s\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003earr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; one, two, three\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow I realise this is a partially stupid example, but it serves to explain the point I wanted to make. If you were complaining about my choice of printf earlier, here is the way I think most rubyists would solve this problem instead.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003earr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;one\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;two\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;three\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003earr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt; one two three\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd if I wanted to be slightly cleverer with the \u003ccode\u003eprintf\u003c/code\u003e version, and print out an array containing an unknown number of strings, but of a set width, then I could do the following. \u003cem\u003e(NB: This is actually how I ran into this problem.)\u003c/em\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003earr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;one\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;two\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;three\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprintf\u003c/span\u003e \u003cspan class=\"n\"\u003earr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;%6s\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003earr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026gt;\u0026gt;    one   two three\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd that is where the lowly \u003ccode\u003e*\u003c/code\u003e comes in.\u003c/p\u003e\n"},{"title":"Merry Testing","date_published":"2008-12-25T15:01:54Z","date_modified":"2008-12-25T15:01:54Z","id":"https://caiustheory.com/merry-testing/","url":"https://caiustheory.com/merry-testing/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eJust a few examples of the same test written in a few languages. Its testing setting the date on an object that is created in the tests\u0026rsquo; setup method already. These fall under the unit testing, rather than full-stack testing.\u003c/p\u003e\n\u003ch3 id=\"testing-in-objc-with-ocunithttpwwwsentechsoftwareocunit\"\u003eTesting in ObjC with \u003ca href=\"http://www.sente.ch/software/ocunit/\"\u003eOCUnit\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-objc\" data-lang=\"objc\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Add a date and time\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"p\"\u003e-\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"nf\"\u003etestSettingDate\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eNSDate\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003etheDate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eNSDate\u003c/span\u003e \u003cspan class=\"n\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eSTAssertNoThrow\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"n\"\u003ecalc\u003c/span\u003e \u003cspan class=\"nl\"\u003esetDate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003etheDate\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"s\"\u003e@\u0026#34;Shouldn\u0026#39;t raise an exception\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// And it should match when pulled out as well\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"n\"\u003eSTAssertEqualObjects\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"n\"\u003ecalc\u003c/span\u003e \u003cspan class=\"n\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003etheDate\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                         \u003cspan class=\"s\"\u003e@\u0026#34;%@ should match %@\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                         \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ecalc\u003c/span\u003e \u003cspan class=\"n\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003etheDate\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"testing-in-ruby-using-rspechttprspecinfo\"\u003eTesting in Ruby using \u003ca href=\"http://rspec.info/\"\u003eRSpec\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eit\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;should set the date successfully\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ethe_date\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eDate\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etoday\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@calc\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ethe_date\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e# And it should match when pulled out as well\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@calc\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edate\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshould\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003ethe_date\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"testing-in-ruby-using-testunithttpwwwruby-docorgstdliblibdoctestunitrdocclassestestunithtml\"\u003eTesting in Ruby using \u003ca href=\"http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html\"\u003eTest::Unit\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003etest_setting_date\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ethe_date\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eDate\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etoday\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@calc\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ethe_date\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e# And it should match when pulled out as well\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eassert_equal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vi\"\u003e@calc\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ethe_date\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"testing-in-php-using-phpunithttpphpunit\"\u003eTesting in PHP using \u003ca href=\"http://phpun.it/\"\u003ePHPUnit\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-php\" data-lang=\"php\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003etestSettingDate\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003e$date\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003e$calc\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\u003cspan class=\"na\"\u003edate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nv\"\u003e$date\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# And it should match when pulled out as well\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"nv\"\u003e$this\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\u003cspan class=\"na\"\u003eassertEquals\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$calc\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\u003cspan class=\"na\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$date\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"Think Visibility: An Online Marketing Conference","date_published":"2008-12-20T13:48:05Z","date_modified":"2008-12-20T13:48:05Z","id":"https://caiustheory.com/think-visibility-an-online-marketing-conference/","url":"https://caiustheory.com/think-visibility-an-online-marketing-conference/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eNow I\u0026rsquo;m not one for blogging about events usually—if I\u0026rsquo;m attending one then I\u0026rsquo;ll just talk about it on \u003ca href=\"http://twitter.com/caius\"\u003etwitter\u003c/a\u003e quite a bit beforehand. However, seeing as this one is being organised by my housemate and I like to keep him in a good mood so he doesn\u0026rsquo;t do something daft like change the locks, I figured I\u0026rsquo;d blog about this one. \u003cem\u003e(Also its really rather a good idea, I\u0026rsquo;ll be paying to attend and think he\u0026rsquo;s bloody mad to organise a conference!)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eThe conference is \u003ca href=\"http://www.thinkvisibility.com/\"\u003eThink Visibility\u003c/a\u003e, which is a\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eone-day mini conference with a focus on the areas of web development and marketing which are usually left behind in the creation process: SEO, PPC, Monetisation, Blogging, Accessibility and Usability.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eThe \u003ca href=\"http://www.thinkvisibility.com/speakers.html\"\u003espeaker line-up\u003c/a\u003e has rather a lot of big names in it if you follow the SEO/Online marketing world, and its only £30 to turn up for the day and listen to them speak. Oh, and theres an afterparty with free drinks \u003cem\u003e(Sponsor permitting)\u003c/em\u003e where you can get drunk with your hero\u0026rsquo;s. (Or something.)\u003c/p\u003e\n\u003cp\u003eMe, I\u0026rsquo;m attending simply because up until a year or so ago I thought SEO was a complete heap of crap, but having known \u003ca href=\"http://www.thehodge.co.uk/\"\u003eDom\u003c/a\u003e for a while, and worked for an SEO agency, I\u0026rsquo;m starting to appreciate that there is an art to it, and it is a much needed skill to run a successful website.\u003c/p\u003e\n"},{"title":"Fix Mail.app crashing after 10.5.6 upgrade","date_published":"2008-12-17T13:58:04Z","date_modified":"2008-12-17T13:58:04Z","id":"https://caiustheory.com/fix-mailapp-crashing-after-1056-upgrade/","url":"https://caiustheory.com/fix-mailapp-crashing-after-1056-upgrade/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eWhen you upgrade to Mac OS 10.5.6, Mail.app might start crashing a few seconds after starting due to the GPG Bundle.\u003c/p\u003e\n\u003cp\u003eThe solution is to grab the updated version of the \u003ca href=\"http://www.sente.ch/software/GPGMail/English.lproj/GPGMail.html\"\u003eGPG bundle\u003c/a\u003e – \u003ca href=\"http://www.sente.ch/pub/beta/GPGMail_d55_Leopard.dmg\"\u003eGPGMail_d55_Leopard.dmg\u003c/a\u003e\u003c/p\u003e\n"},{"title":"Quick Picture of Yourself","date_published":"2008-12-16T18:12:53Z","date_modified":"2008-12-16T18:12:53Z","id":"https://caiustheory.com/quick-picture-of-yourself/","url":"https://caiustheory.com/quick-picture-of-yourself/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eTheres a meme going round the \u003ca href=\"http://www.google.com/search?q=Take+a+picture+of+yourself+right+now\"\u003eblogosphere\u003c/a\u003e/\u003ca href=\"http://search.twitter.com/search?q=Take+a+picture+of+yourself+right+now\"\u003etwitterverse\u003c/a\u003e recently, so I figured I\u0026rsquo;d jump on it because there aren\u0026rsquo;t any pictures of me on this blog yet. And there aren\u0026rsquo;t that many in my \u003ca href=\"http://flickr.com/photos/caius/\"\u003eflickr\u003c/a\u003e photostream either actually.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"http://caius.name/images/qs/Me.png\" alt=\"Picture of Caius\" title=\"Caius Durling\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e\u003ca href=\"http://caius.name/images/qs/Me.png\"\u003eFullsize Picture\u003c/a\u003e\u003c/em\u003e\u003c/p\u003e\n\u003ch2 id=\"instructions\"\u003eInstructions\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eTake a picture of yourself right now.\u003c/li\u003e\n\u003cli\u003eDon’t change your clothes, don’t fix your hair… just take a picture.\u003c/li\u003e\n\u003cli\u003ePost that picture with NO editing.\u003c/li\u003e\n\u003cli\u003ePost these instructions with your picture.\u003c/li\u003e\n\u003c/ol\u003e\n"},{"title":"Installing Ubuntu on an iMac G3","date_published":"2008-12-09T08:51:48Z","date_modified":"2008-12-09T08:51:48Z","id":"https://caiustheory.com/installing-ubuntu-on-an-imac-g3/","url":"https://caiustheory.com/installing-ubuntu-on-an-imac-g3/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI decided to install ubuntu onto my iMac G3\u003c!-- raw HTML omitted --\u003e450Mhz G3, 768mb ram, 20GB Hard Drive\u003c!-- raw HTML omitted --\u003e to play around with.  Only problem was it would boot so far, then just stop at a black screen.  In googling the fix, the blog post that contains the fix is \u003cem\u003eslightly\u003c/em\u003e outdated and 100% 404.\u003c/p\u003e\n\u003cp\u003eHere is the fix, updated for Ubuntu 6.10 Desktop PPC:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eWhen the screen goes black, drop to the console\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e Control - Option - F2\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cem\u003e(if you need to log in use the name ubuntu to log in.)\u003c/em\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e $ sudo nano /etc/X11/xorg.conf\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eChange the frequencies in monitor section as follows:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e Section “Monitor”\n     Identifier “Generic Monitor”\n     Option “DPMS”\n     HorizSync 60-60\n     VertRefresh 43-117\n EndSection\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eAfter the changes then type \u003ccode\u003econtrol-o\u003c/code\u003e, \u003ccode\u003ereturn\u003c/code\u003e (to accept the filename), then \u003ccode\u003econtrol-x\u003c/code\u003e (save and exit nano)\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eRestart X by running the following:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e sudo killall gdm \u0026amp;\u0026amp; sudo /etc/init.d/gdm start\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n"},{"title":"About","date_published":"2008-12-07T04:09:28Z","date_modified":"2008-12-07T04:09:28Z","id":"https://caiustheory.com/about/","url":"https://caiustheory.com/about/","author":{"name":"Caius Durling"},"content_html":"\u003ch2 id=\"the-domain-name\"\u003eThe Domain Name\u003c/h2\u003e\n\u003cp\u003eThis blog used to be located at \u003ca href=\"http://swedishcampground.com/\"\u003eSwedishCampground.com\u003c/a\u003e, but I ended up with a better use for that name, so this got sidelined over here. I bought \u003ca href=\"http://caiustheory.com/\"\u003eCaiusTheory.com\u003c/a\u003e after having someone mention it to me in parody of Chaos Theory, and decided it was fairly apt for my musings and technical waffling.\u003c/p\u003e\n\u003ch2 id=\"the-author\"\u003eThe Author\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m a:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eMac User\u003c/li\u003e\n\u003cli\u003eDeveloper at \u003ca href=\"http://freeagent.com/\"\u003eFreeAgent\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://twitter.com/caius\"\u003eTwitter Addict\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFind out more about me over on my \u003ca href=\"http://caius.name/\"\u003eprofile site\u003c/a\u003e.\u003c/p\u003e\n"},{"title":"When work just feels right","date_published":"2008-11-21T11:51:08Z","date_modified":"2008-11-21T11:51:08Z","id":"https://caiustheory.com/when-work-just-feels-right/","url":"https://caiustheory.com/when-work-just-feels-right/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eMuch like \u003ca href=\"http://www.3hv.co.uk/\" title=\"3hv\"\u003eRahoul\u0026rsquo;s\u003c/a\u003e post on \u003ca href=\"http://www.3hv.co.uk/blog/2008/10/16/working-for-brightbox/\" title=\"Working for Brightbox\"\u003eknowing you\u0026rsquo;re on the right path\u003c/a\u003e, I had that moment this morning whilst we were discussing a future feature for our control panel.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003ejohn\u003c/strong\u003e: will I be able to superpoke our customers?\u003cbr\u003e\n\u003cstrong\u003ejohn\u003c/strong\u003e: is that in the spec?\u003cbr\u003e\n\u003cstrong\u003eJeremy\u003c/strong\u003e: \u0026ldquo;John made server10 a zombie\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003eCaius\u003c/strong\u003e: \u0026ldquo;server10 zombified server9\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003eJeremy\u003c/strong\u003e: \u0026ldquo;David has poked server19 19234 times\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003ejohn\u003c/strong\u003e: server16 messaged David \u0026ldquo;My disk is filling up and I have files I need to put somewhere, please help!\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003eCaius\u003c/strong\u003e: \u0026ldquo;Rahoul ended his friendship with server15\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003eDavid\u003c/strong\u003e: \u0026ldquo;server02 is now married to server05\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003eRahoul\u003c/strong\u003e: server10 has sent you 12 videos of a fat man eating a cake\u003cbr\u003e\n\u003cstrong\u003eDavid\u003c/strong\u003e: server11 joined the group \u0026ldquo;KVM ftw\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003eCaius\u003c/strong\u003e: \u0026ldquo;server16 threw a sheep at server15\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003ejohn\u003c/strong\u003e: server03 joined the group \u0026ldquo;Centos sucks!\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003eCaius\u003c/strong\u003e: \u0026ldquo;server15 sent an emergency broadcast: physical movement detected\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003ejohn\u003c/strong\u003e: storage03 has logged off\u003cbr\u003e\n\u003cstrong\u003eCaius\u003c/strong\u003e: \u0026ldquo;x13 flew to the moon 0 times\u0026rdquo;\u003cbr\u003e\n\u003cstrong\u003ejohn\u003c/strong\u003e: disk5 in storage02 is now a zombie\u003cbr\u003e\n\u003cstrong\u003eJeremy\u003c/strong\u003e: disk4 in storage02 is now a zombie\u003cbr\u003e\n\u003cstrong\u003eJeremy\u003c/strong\u003e: disk3 in storage02 is now a zombie\u003cbr\u003e\n\u003cstrong\u003eJeremy\u003c/strong\u003e: disk2 in storage02 is now a zombie\u003cbr\u003e\n\u003cstrong\u003ejohn\u003c/strong\u003e: storage02 was sold on ebay by Jeremy\u003cbr\u003e\n\u003cstrong\u003eCaius\u003c/strong\u003e: \u0026ldquo;john was sold on brightbox marketplace by storage5\u0026rdquo;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cem\u003e(In case you don\u0026rsquo;t know, I work for \u003ca href=\"http://www.brightbox.co.uk/\"\u003eBrightbox\u003c/a\u003e.)\u003c/em\u003e\u003c/p\u003e\n"},{"title":"Adding a remote to existing git repo","date_published":"2008-11-09T18:32:55Z","date_modified":"2008-11-09T18:32:55Z","id":"https://caiustheory.com/adding-a-remote-to-existing-git-repo/","url":"https://caiustheory.com/adding-a-remote-to-existing-git-repo/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eUsually for me this happens when I have an existing project and I setup a \u003ca href=\"http://github.com/\"\u003egithub\u003c/a\u003e repo for it. As part of the setup for the github project, it gives you the commands to run to add the github repo as a remote to my local git repo.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ecd existing_git_repo\ngit remote add origin git@github.com:caius/foo.git\ngit push origin master\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThe problem then is you\u0026rsquo;ve added the remote account, but the local master branch isn\u0026rsquo;t tracking the remote master branch, so when you try and just \u003ccode\u003egit pull\u003c/code\u003e it will fail with a message telling you to set the remote refs up.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$ git pull  \nYou asked me to pull without telling me which branch you  \nwant to merge with, and 'branch.master.merge' in  \nyour configuration file does not tell me either.  Please  \nname which branch you want to merge on the command line and  \ntry again (e.g. 'git pull \u0026lt;repository\u0026gt; \u0026lt;refspec\u0026gt;').  \nSee git-pull(1) for details on the refspec.  \n\nIf you often merge with the same branch, you may want to  \nconfigure the following variables in your configuration  \nfile:\n\n    branch.master.remote = \u0026lt;nickname\u0026gt;\n    branch.master.merge = \u0026lt;remote-ref\u0026gt;\n    remote.\u0026lt;nickname\u0026gt;.url = \u0026lt;url\u0026gt;\n    remote.\u0026lt;nickname\u0026gt;.fetch = \u0026lt;refspec\u0026gt;\n\nSee git-config(1) for details.\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThe answer is to do what it says funnily enough, and add the remote refs tracking to the config file. The easiest way I\u0026rsquo;ve found of doing this is to edit \u003ccode\u003e.git/config\u003c/code\u003e and add the following at the bottom of it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e[branch \u0026#34;master\u0026#34;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"na\"\u003eremote\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003eorigin\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    merge = refs/heads/master\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eRemember to change the branch or remote names if you need to.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eOnce you\u0026rsquo;ve added that to the config you can run \u003ccode\u003egit pull\u003c/code\u003e on the master branch and it\u0026rsquo;ll do the usual automagical thing and pull the remote master branch changes into the local one!\u003c/p\u003e\n\u003ch4 id=\"updated-2008-11-09\"\u003eUpdated 2008-11-09\u003c/h4\u003e\n\u003cp\u003eSee CiarÃƒÂ¡n\u0026rsquo;s comment below for an all-inclusive command to do the above.\u003c/p\u003e\n"},{"title":"Removing non-existent source from rubygems","date_published":"2008-11-07T06:08:28Z","date_modified":"2008-11-07T06:08:28Z","id":"https://caiustheory.com/removing-non-existant-source-from-rubygems/","url":"https://caiustheory.com/removing-non-existant-source-from-rubygems/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI just came to move some ruby scripts onto my mac mini, and to do so I needed to install a couple of gems. Now I realised I hadn\u0026rsquo;t installed or updated rubygems on the machine for a while, so I figured it was best to update \u003ccode\u003egem\u003c/code\u003e before installing the gems I wanted. Easier said than done.\u003c/p\u003e\n\u003cp\u003eAt some point in the past I had added \u003ccode\u003ehttp://gems.datamapper.org\u003c/code\u003e as a source to rubygems. Since then the datamapper project has discontinued using this gem source to serve up gems, so I was getting the following output:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003emm:daemons caius$ sudo gem update --system\nUpdating installed gems\nBulk updating Gem source index for: http://gems.rubyforge.org/\nERROR:  While executing gem ... (Gem::RemoteSourceException)\n    HTTP Response 404 fetching http://gems.datamapper.org/yaml\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eEeek! I can\u0026rsquo;t update because the source no longer exists. So I figured I\u0026rsquo;d remove the source before updating, that should work right? Wrong. It updates the sources before removing the source from the config it would appear.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003emm:daemons caius$ sudo gem sources\n** CURRENT SOURCES ***\n\nhttp://gems.rubyforge.org\nhttp://gems.datamapper.org\n\nmm:daemons caius$ sudo gem sources -r http://gems.datamapper.org\nBulk updating Gem source index for: http://gems.rubyforge.org/\nERROR:  While executing gem ... (Gem::RemoteSourceException)\n    HTTP Response 404 fetching http://gems.datamapper.org/yaml\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eOh balls. So how do I remove the source without updating it first. I need to update it to remove it, but to remove it I need to update from it. Gotta love catch 22s!\u003c/p\u003e\n\u003cp\u003eI remembered that \u003ccode\u003egem install\u003c/code\u003e has an option not to update sources, \u003ccode\u003e--no-update-sources\u003c/code\u003e. So I figured thats gotta work when removing a source as well, but it doesn\u0026rsquo;t.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003emm:daemons caius$ sudo gem sources -r http://gems.datamapper.org --no-update-sources\nERROR:  While executing gem ... (OptionParser::InvalidOption)\n    invalid option: --no-update-sources\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eOh crap. Now what do I do? Take my usual tactic and google for a hint of course! I\u0026rsquo;d considered trying to find where the gem config was and remove the source by hand, but I figured that wouldn\u0026rsquo;t be that simple. After hitting a couple of sites that weren\u0026rsquo;t relevant I ended up \u003ca href=\"http://jaigouk.blogspot.com/2008/07/404-fetching-httpgemsdatamapperorgyaml.html\"\u003eon the edge of complexity\u003c/a\u003e where he mentions the command \u003ccode\u003enano ~/.gemrc\u003c/code\u003e. Which made me wonder if that file contains the sources.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003emm:daemons caius$ cat ~/.gemrc\n--- \n:update_sources: true\n:verbose: true\n:bulk_threshold: 1000\n:sources: \n- http://gems.rubyforge.org\n- http://gems.datamapper.org\n:backtrace: false\n:benchmark: false\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAll I needed to do was remove the \u003ccode\u003e- http://gems.datamapper.org\u003c/code\u003e line and \u003cem\u003epoof\u003c/em\u003e, \u003ccode\u003egem\u003c/code\u003e was working again. One quick \u003ccode\u003egem update --system\u003c/code\u003e later and I was upgraded from gem 1.1.1 to 1.3.1 and installing the gems I needed.\u003c/p\u003e\n"},{"title":"Setting up git with rails apps","date_published":"2008-11-06T13:03:02Z","date_modified":"2008-11-06T13:03:02Z","id":"https://caiustheory.com/setting-up-git-with-rails-apps/","url":"https://caiustheory.com/setting-up-git-with-rails-apps/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eWhen I create a new rails app, I\u0026rsquo;m constantly going back to another project and stealing the \u003ccode\u003e.gitignore\u003c/code\u003e file from it to make sure that git doesn\u0026rsquo;t know about certain files rails either updates frequently, or stores machine-specific data in. The latter is generally just \u003ccode\u003econfig/database.yml\u003c/code\u003e, because I develop alongside my colleagues at \u003ca href=\"http://brightbox.co.uk/\" title=\"Brightbox - Serious Rails Hosting\"\u003eBrightbox\u003c/a\u003e and we deploy via \u003ca href=\"http://www.capify.org/\"\u003ecapistrano\u003c/a\u003e, we always put the \u003ccode\u003edatabase.yml\u003c/code\u003e file in the shared directory on the server, so we each have our own version with our local credentials in it locally. And thus we don\u0026rsquo;t want it to be tracked by git.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s what I\u0026rsquo;ve collated from various sources over the few weeks I\u0026rsquo;ve been using git + rails everyday.\u003c/p\u003e\n\u003ch4 id=\"gitignore\"\u003e.gitignore\u003c/h4\u003e\n\u003cpre\u003e\u003ccode\u003econfig/database.yml\nlog/*.log\ntmp/*\n\n# OS X only\n.DS_Store\n**/.DS_Store\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen to make sure \u003ccode\u003elog/\u003c/code\u003e and \u003ccode\u003etmp/\u003c/code\u003e are tracked, convention is to add a blank \u003ccode\u003e.gitkeep\u003c/code\u003e file in them.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003etouch log/.gitkeep\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003etouch tmp/.gitkeep\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"Spanning Sync","date_published":"2008-11-03T11:40:04Z","date_modified":"2008-11-03T11:40:04Z","id":"https://caiustheory.com/spanning-sync/","url":"https://caiustheory.com/spanning-sync/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo for \u003ca href=\"http://www.brightbox.co.uk\" title=\"Brightbox - Serious rails hosting\"\u003ework\u003c/a\u003e I have to subscribe to a shared google calendar the company uses. Annoyingly this can\u0026rsquo;t be through my Google Apps account though, it has to be through my normal google account.\u003c/p\u003e\n\u003cp\u003eAlso annoying is the fact google doesn\u0026rsquo;t let me subscribe to my shared calendars without making them 100% public. Which means the shared private calendar work uses would have to be a \u003cem\u003epublic\u003c/em\u003e shared calendar, which is obviously not going to happen. (Can\u0026rsquo;t have everyone knowing when the secret drinking parties are!)\u003c/p\u003e\n\u003cp\u003eThankfully, there is already a solution out there to this problem, and it comes in the form of a Preference Pane called \u003ca href=\"http://spanningsync.com/\" title=\"Spanning Sync\"\u003eSpanningSync\u003c/a\u003e that syncs iCal to Google Calendar. All you need to do is install the prefpane and enter your google email/password (which is then safely secured in your keychain I believe.)\u003c/p\u003e\n\u003cp\u003eWhat it does is stick an icon in the menubar, and then at a pre-defined (and customisable) interval, sync any new changes between the local iCal calendar and the remote shared calendar in your google acccount.\u003c/p\u003e\n\u003cp\u003eSpanning Sync is only $25 to register for the year, but you can save yourself $5 by entering \u003cstrong\u003e6EMQAC\u003c/strong\u003e as the spanning sync coupon code, or \u003ca href=\"http://spanningsync.com/?r=6EMQAC\"\u003eclicking here\u003c/a\u003e\u003c/p\u003e\n"},{"title":"Create a blank rails app including plugins","date_published":"2008-11-03T06:48:27Z","date_modified":"2008-11-03T06:48:27Z","id":"https://caiustheory.com/create-a-blank-rails-app-including-plugins/","url":"https://caiustheory.com/create-a-blank-rails-app-including-plugins/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eWhen I create a rails app from scratch I like to include certain plugins to help me write the app, such as the \u003cem\u003eRspec\u003c/em\u003e testing framework instead of the built-in \u003cem\u003eTest::Unit\u003c/em\u003e and \u003cem\u003ejQuery\u003c/em\u003e instead of \u003cem\u003eprototype\u003c/em\u003e.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://rspec.info/\"\u003eRspec\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://github.com/rahoulb/rspec-rails/wikis\"\u003eRspec-rails\u003c/a\u003e \u003cem\u003e(NB: I use \u003ca href=\"http://3hv.co.uk\"\u003erahoul\u003c/a\u003e\u0026rsquo;s fork of rspec-rails)\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://github.com/aslakhellesoy/cucumber/wikis\"\u003eCucumber\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://github.com/brynary/webrat/wikis\"\u003eWebrat\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://ennerchi.com/projects/jrails\"\u003ejRails\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://plugins.code.lukeredpath.co.uk/browser/demeters_revenge/trunk\"\u003eDemeters Revenge\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAnd here are the commands in the order I run them to create the blank app.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Create the rails app\ncd ~/Sites/apps/\nrails myapp\ncd myapp\n\n# Setup a git repo\ngit init\n# Add all files and make the initial import\ngit add .\ngit commit -m \u0026quot;Initial Import\u0026quot;\n\n# Add the plugins as git submodules\ngit submodule add git://github.com/dchelimsky/rspec.git vendor/plugins/rspec\ngit submodule add git://github.com/rahoulb/rspec-rails.git vendor/plugins/rspec-rails\ngit submodule add git://github.com/aslakhellesoy/cucumber.git vendor/plugins/cucumber\ngit submodule add git://github.com/brynary/webrat.git vendor/plugins/webrat\ngit submodule add git://github.com/caius/demeters_revenge.git vendor/plugins/demeters_revenge\n\n# Commit the changes\ngit ci -am \u0026quot;Adding all needed submodules\u0026quot;\n\n# Replace TestUnit with rspec\ngit rm -r test/\nruby script/generate rspec\n# Replace stories with cucumber features\nrm -rf stories/\nruby script/generate cucumber\n\n# Add the changes to git\ngit add .\ngit ci -m \u0026quot;Committing initial rspec/cucumber files\u0026quot;\n\n# Install jRails, we have to install it using script/plugin\n# Remove existing javascript files\ngit rm public/javascripts/*\nmkdir public/javascripts\n# Add jrails\nruby script/plugin install http://ennerchi.googlecode.com/svn/trunk/plugins/jrails\ngit add vendor/plugins/jrails/ public/javascripts\ngit ci -m \u0026quot;Adding jRails to replace Prototype\u0026quot;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd now you have a blank app waiting for you to write using features for full stack testing, and rspec for testing model and controller code.\u003c/p\u003e\n\u003ch4 id=\"updated-2008-11-04\"\u003eUpdated 2008-11-04\u003c/h4\u003e\n\u003cp\u003eAdded demeters revenge and jRails plugins.\u003c/p\u003e\n\u003ch4 id=\"update-2008-11-05\"\u003eUpdate 2008-11-05\u003c/h4\u003e\n\u003cp\u003eI\u0026rsquo;ve also blogged the \u003ca href=\"http://swedishcampground.com/setting-up-git-with-rails-apps\"\u003e.gitignore\u003c/a\u003e file I use with rails apps as well. Usually add it into my apps before running \u003ccode\u003egit init\u003c/code\u003e\u003c/p\u003e\n"},{"title":"Keyboards","date_published":"2008-10-14T13:59:01Z","date_modified":"2008-10-14T13:59:01Z","id":"https://caiustheory.com/keyboards/","url":"https://caiustheory.com/keyboards/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eBack in the day I swapped the keys on my 12\u0026quot; powerbook keyboard around to read \u003ccode\u003emacgenius\u003c/code\u003e across the middle row.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://www.flickr.com/photos/caius/2941719496/\"\u003e\u003cimg src=\"http://farm4.static.flickr.com/3213/2941719496_caf2a6a813_m.jpg\" alt=\"Powerbook Keyboard\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eI unearthed the picture, and figured, why not do it to my apple aluminium keyboard? So I found a \u003ca href=\"http://skeltoac.com/2007/10/22/apple-keyboard-aluminum-keycap-removal/\"\u003etutorial\u003c/a\u003e from some other guy that\u0026rsquo;d done it, and dug out my penknife.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://www.flickr.com/photos/caius/2938651260/\"\u003e\u003cimg src=\"http://farm4.static.flickr.com/3243/2938651260_915f42d92d_m.jpg\" alt=\"External Keyboard\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eAfter that I decided to rearrange the macbook internal keyboard as well. First I googled around to make sure lifting the keys was the same as doing it on the external keyboard (which it appeared to be), then I went ahead and rearranged them as well.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://www.flickr.com/photos/caius/2940872039/\"\u003e\u003cimg src=\"http://farm4.static.flickr.com/3214/2940872039_164ee672ef_m.jpg\" alt=\"Internal Keyboard \u0026ndash; Macgenius\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eSo whilst I was wondering what to do about it, my mother emailed me and suggested using \u003ccode\u003eontherails\u003c/code\u003e instead of \u003ccode\u003emacgenius\u003c/code\u003e. So I did, and now the top row reads \u003ccode\u003eontherails\u003c/code\u003e on the macbooks\u0026rsquo; internal keyboard.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://www.flickr.com/photos/caius/2941726222/\"\u003e\u003cimg src=\"http://farm4.static.flickr.com/3146/2941726222_67b2405a89_m.jpg\" alt=\"Internal Keyboard \u0026ndash; Ontherails\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eAll pictures are licenced under \u003ca href=\"http://creativecommons.org/licenses/by/2.0/deed.en_GB\"\u003eCreative Commons Attribution 2.0 Generic licence\u003c/a\u003e and the above pictures, plus some \u003cem\u003ein progress\u003c/em\u003e shots, are available in my \u003ca href=\"http://www.flickr.com/photos/caius/sets/72157608015895484/\"\u003eKeyboard Modifications\u003c/a\u003e flickr set.\u003c/p\u003e\n"},{"title":"Alistapart Survey","date_published":"2008-07-29T14:47:24Z","date_modified":"2008-07-29T14:47:24Z","id":"https://caiustheory.com/alistapart-survey/","url":"https://caiustheory.com/alistapart-survey/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003c!-- raw HTML omitted --\u003e\u003c!-- raw HTML omitted --\u003e\u003c!-- raw HTML omitted --\u003e\u003c/p\u003e\n"},{"title":"Another Concise Code Example","date_published":"2008-04-26T15:54:09Z","date_modified":"2008-04-26T15:54:09Z","id":"https://caiustheory.com/another-concise-code-example/","url":"https://caiustheory.com/another-concise-code-example/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eThis is just another example showing how I refactor code down to its bare minimum. The reason why I do this so much (and indeed I think why ruby is so easy to read compared to other languages) is because it makes my code more readable and less of a bugger to pick up after a while.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003ePage\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kp\"\u003eattr_accessor\u003c/span\u003e \u003cspan class=\"ss\"\u003e:parent_id\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eold_parent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparent?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"no\"\u003ePage\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparent_id\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eparent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"no\"\u003ePage\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparent_id\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparent?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eold_parent\u003c/code\u003e and \u003ccode\u003eparent\u003c/code\u003e return exactly the same, but one is 2 lines compared to 5 and easier to read.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUpdate:\u003c/strong\u003e \u003ca href=\"http://ciaranwal.sh/\"\u003eCiaran\u003c/a\u003e pointed out that the Page.parent method would only ever return false. Added the return statement to it to fix the bug.\u003c/p\u003e\n"},{"title":"Inaugural Habaricon","date_published":"2008-04-01T07:27:55Z","date_modified":"2008-04-01T07:27:55Z","id":"https://caiustheory.com/inaugural-habaricon/","url":"https://caiustheory.com/inaugural-habaricon/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo I managed to secure a place to \u003ca href=\"http://habaricon.com/\"\u003eHabariCon \u0026lsquo;08\u003c/a\u003e. Not quite sure what to expect, but being able to meet other people in real life and talk about habari \u0026amp; related things all day will be quite cool.\u003c/p\u003e\n\u003cp\u003eExpect a round-up post of the \u0026lsquo;con tonight!\u003c/p\u003e\n"},{"title":"BarCamp Manchester","date_published":"2008-03-01T11:34:25Z","date_modified":"2008-03-01T11:34:25Z","id":"https://caiustheory.com/barcamp-manchester/","url":"https://caiustheory.com/barcamp-manchester/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo I\u0026rsquo;m at BarCamp Manchester for the day.  Its great meeting people I already know, and meeting new ones; most impressed by \u003ca href=\"http://petercooper.co.uk/\"\u003ePeter\u003c/a\u003e\u0026rsquo;s ruby shoes.. they glitter!\u003c/p\u003e\n\u003cp\u003eSo heres a little list of links for the day:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://www.flickr.com/photos/tags/BarCampManchesterUk/\"\u003ehttp://www.flickr.com/photos/tags/BarCampManchesterUk/\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://barcamp.org/BarCampManchesterUk\"\u003ehttp://barcamp.org/BarCampManchesterUk\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n"},{"title":"Command line tricks: Scripting Languages","date_published":"2008-02-14T16:24:27Z","date_modified":"2008-02-14T16:24:27Z","id":"https://caiustheory.com/command-line-tricks--scripting-languages/","url":"https://caiustheory.com/command-line-tricks--scripting-languages/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eTo search your php.ini file quickly and easily with the option to use regular expressions, I tend to drop back to the \u003c!-- raw HTML omitted --\u003ecli\u003c!-- raw HTML omitted --\u003e.  The reason for this is I can easily parse the output of \u003ccode\u003ephpinfo()\u003c/code\u003e with \u003ccode\u003egrep\u003c/code\u003e, and can do various things with the output, could even pass it to a script if I really wanted to.\u003c/p\u003e\n\u003cp\u003eHere is the line I use to search \u003ccode\u003ephpinfo()\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;?php phpinfo() ?\u0026gt;\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e php \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep -i \u003cspan class=\"nv\"\u003e$search_string\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt passes the string through the php interpreter and then searches through it with grep.\u003c/p\u003e\n\u003cp\u003eYou can also do other nifty things with the shell \u0026amp; php + ruby especially (though I imagine python \u0026amp; perl work in the same way.) For instance I wanted to see if the following ruby would return the number of seconds since the \u003ca href=\"http:/en.wikipedia.org/wiki/Unix_Time\"\u003eepoch\u003c/a\u003e till now.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"no\"\u003eTime\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enow\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow I could fire up a PHP page and do something like the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-php\" data-lang=\"php\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u003c/span\u003e\u003cspan class=\"nx\"\u003ephp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;php: \u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"nx\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ruby: \u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"sb\"\u003e`ruby -e \u0026#39;print Time.now.to_i\u0026#39;`\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e?\u0026gt;\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBut what if I\u0026rsquo;ve not got a web server with PHP running on the machine I\u0026rsquo;m using? Well then I could drop back to the shell and run it through \u003ccode\u003ephp\u003c/code\u003e using \u003ccode\u003ecat\u003c/code\u003e as a way to insert multiple lines, and it would look like the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecat \u003cspan class=\"s\"\u003e\u0026lt;\u0026lt;PHP | php\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026lt;?php\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e  echo \u0026#34;php: \u0026#34; . time() . \u0026#34;\\n\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e  echo \u0026#34;ruby: \u0026#34; . `ruby -e \u0026#39;print Time.now.to_i\u0026#39;` . \u0026#34;\\n\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003ePHP\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ephp: \u003cspan class=\"m\"\u003e1203004463\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eruby: \u003cspan class=\"m\"\u003e1203004463\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow this works, but why do I want to remember all that php, and seeing as I have to drop back to the shell to access the ruby statement, why not just let the shell do all the work? So after a few seconds thinking, I came up with this\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eruby -e \u003cspan class=\"s1\"\u003e\u0026#39;puts \u0026#34;ruby: #{Time.now.to_i}\u0026#34;\u0026#39;\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003e  \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;?php echo \u0026#34;PHP: \u0026#34; . time() . \u0026#34;\\n\u0026#34; ?\u0026gt;\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e php\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis runs the ruby code through \u003ccode\u003eruby\u003c/code\u003e and the php code through \u003ccode\u003ephp\u003c/code\u003e without dropping back to the shell from within a language interpreter :)\u003c/p\u003e\n\u003ch3 id=\"update\"\u003eUpdate:\u003c/h3\u003e\n\u003cp\u003eFangel pointed out \u003ccode\u003ephp -r\u003c/code\u003e is the equivilent of \u003ccode\u003eruby -e\u003c/code\u003e so the final commands could just be:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eruby -e \u003cspan class=\"s1\"\u003e\u0026#39;puts \u0026#34;ruby: #{Time.now.to_i}\u0026#34;\u0026#39;\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"se\"\u003e\u003c/span\u003ephp -r \u003cspan class=\"s1\"\u003e\u0026#39;echo \u0026#34;PHP: \u0026#34;.time().\u0026#34;\\n\u0026#34;;\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"},{"title":"Refactoring code logically","date_published":"2008-02-12T12:28:38Z","date_modified":"2008-02-12T12:28:38Z","id":"https://caiustheory.com/refactoring-code-logically/","url":"https://caiustheory.com/refactoring-code-logically/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eAnd now an example of how I write my ruby code and get it down to the bare, readable, minimum code needed. This is real life code taken from a website I\u0026rsquo;m building, but I\u0026rsquo;ve changed the objects to a blog post because more people will relate to that easier.\u003c/p\u003e\n\u003cp\u003eThe show object has an id passed in using the \u003ccode\u003eparams\u003c/code\u003e Hash, I want to check if that post exists in the database first.  If it does, then render the page, and if it doesn\u0026rsquo;t return a 404 error page.\u003c/p\u003e\n\u003cp\u003eSo I start off by writing this in \u003cem\u003elonghand\u003c/em\u003e ruby, I\u0026rsquo;m using the \u003ca href=\"http://merbivore.com/\"\u003emerb\u003c/a\u003e framework with \u003ca href=\"http://datamapper.com/\"\u003eDataMapper\u003c/a\u003e ORM by the way.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"vi\"\u003e@post\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003ePost\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"vi\"\u003e@post\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003erender\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;404 - Not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow whilst theres nothing wrong with this code, it just doesn\u0026rsquo;t look right to me. There is a big if/else statement in there whilst I\u0026rsquo;m sure there doesn\u0026rsquo;t need to be.\u003c/p\u003e\n\u003cp\u003eNow I know if I return at any point in a ruby method, it exits the method at that point. So the first thing to is to refactor the \u003ccode\u003eif\u003c/code\u003e test to remove a line of code. I shall assign \u003ccode\u003e@post\u003c/code\u003e to the result of the DB as the actual if statement\u0026rsquo;s test.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vi\"\u003e@post\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003ePost\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003erender\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;404 - Not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSo thats reading slightly better, and also is a line less of code. Now I wonder if I can use a \u003ccode\u003ereturn true\u003c/code\u003e in there to stop me having to explicitly state an \u003ccode\u003eelse\u003c/code\u003e clause.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vi\"\u003e@post\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epost\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003erender\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;404 - Not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow the eagerest amongst you will be wondering what the advantage of that code is. It doesn\u0026rsquo;t appear any more readable (slightly less in fact as you have to figure out its an implicit else) and is exactly the same amount of lines as the previous example. But what if we change the \u003ccode\u003eif\u003c/code\u003e to an \u003ccode\u003eif !\u003c/code\u003e and flip the code logic around?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"o\"\u003e!\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vi\"\u003e@post\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003ePost\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;404 - not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003erender\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow a raise will stop the code executing, and in the real application you would in fact just redirect to your 404 error page. The problem now is the \u003ccode\u003eif !\u003c/code\u003e looks ugly and isn\u0026rsquo;t easily readable.\u003c/p\u003e\n\u003cp\u003eAll \u003ccode\u003eunless\u003c/code\u003e does is \u003ccode\u003eif !\u003c/code\u003e, that is, if the inverse of the result of the test statement is true, then invoke the block given to it. A quick example for you:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# without unless\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"vi\"\u003e@user\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elogged_in?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Please login.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# using unless\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eunless\u003c/span\u003e \u003cspan class=\"vi\"\u003e@user\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elogged_in?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Please login.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow whilst \u003ccode\u003eif !\u003c/code\u003e doesn\u0026rsquo;t seem that bad compared to \u003ccode\u003eunless\u003c/code\u003e, the readablility of the code increases. It reads more as a flow of logic, and is quicker for the human brain to walk through (my brain anyway!)\u003c/p\u003e\n\u003cp\u003eSo using unless we get 4 lines of code that is easily readable.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eunless\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vi\"\u003e@post\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003ePost\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;404 - Not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003erender\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow what if we go one step further and use the unless shorthand way of testing and exectuting one line of code?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;404 - Not found\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eunless\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vi\"\u003e@post\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003ePost\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003erender\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd that is generally how I write my code logically.  Of course for something simple like this I\u0026rsquo;d probably jump in at the last block having refactored it in my head first, but for more complex things I tend to write them exlicitly and then refactor them down whilst maintaining readability of my code.\u003c/p\u003e\n"},{"title":"Use datamapper sessions with merb \u0026 datamapper","date_published":"2008-02-05T20:27:31Z","date_modified":"2008-02-05T20:27:31Z","id":"https://caiustheory.com/use-datamapper-sessions-with-merb-datamapper/","url":"https://caiustheory.com/use-datamapper-sessions-with-merb-datamapper/","author":{"name":"Caius Durling"},"content_html":"\u003ch3 id=\"issue\"\u003eIssue\u003c/h3\u003e\n\u003cp\u003eCan\u0026rsquo;t use merb sessions with datamapper \u0026amp; mysql, get back an error about needing an id on the text column or something (I had the error a couple of days ago.)\u003c/p\u003e\n\u003ch3 id=\"solution\"\u003eSolution\u003c/h3\u003e\n\u003cp\u003eI suggest grabbing merb_datamapper svn source to fix this in.  To do so make sure you have subversion installed on your machine (I\u0026rsquo;m assuming a Unix based machine here.)\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eCheckout the source\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e svn co http://svn.devjavu.com/merb/plugins/merb_datamapper\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eOpen up the affected file in your favourite editor \u003cem\u003e(I use TextMate)\u003c/em\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e cd merb_datamapper\n mate lib/merb/sessions/data_mapper_session.rb\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFind line 25 that contains\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e `property :session_id, :text, :lazy =\u0026gt; false, :key =\u0026gt; true`\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eand remove \u003ccode\u003e:text, :lazy =\u0026gt; false\u003c/code\u003e to replace it with \u003ccode\u003e:string\u003c/code\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e `property :session_id, :string, :key =\u0026gt; true`\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eSave and close the file, thats the editing done.  Now to install the gem.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eBuild the gem\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e rake gem\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eInstall the gem\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e sudo gem install pkg/merb_datamapper-0.5.gem\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eAnd you\u0026rsquo;re away with the fix installed.  Now just run \u003ccode\u003emerb\u003c/code\u003e to create your sessions table in the db.  Hope this helped!\u003c/p\u003e\n"},{"title":"Why do I love Ruby?","date_published":"2008-01-30T14:15:24Z","date_modified":"2008-01-30T14:15:24Z","id":"https://caiustheory.com/why-do-i-love-ruby/","url":"https://caiustheory.com/why-do-i-love-ruby/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo mother (who can\u0026rsquo;t program) just posed me the question\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eWhy is Ruby your favourite programming language?\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eMe being a show off jumped straight into \u003ca href=\"http://macromates.com/\"\u003eTextMate\u003c/a\u003e and banged out some code in real time to show her.  First up, a quick little one-liner of Ruby code to output a String:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;Hello World\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSo she goes, \u0026ldquo;Sure, but whats so brilliant about that?\u0026rdquo; So I just decide to reverse the string, have it output in reverse order:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereverse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# =\u0026gt; \u0026#34;dlroW olleH\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen the next question comes, \u0026ldquo;So what makes that so much easier than in other languages?\u0026rdquo; Well now I was thinking on the spot about which other language I can bang out a quick example in without having to look up too much information.  PHP seems the logical choice, being the language I know best behind Ruby.\u003c/p\u003e\n\u003cp\u003eThinking about how to do it in PHP, I can\u0026rsquo;t think of a function to reverse the content of a string, but I know that \u003ccode\u003earray_reverse()\u003c/code\u003e exists, so I just split it into an array and reverse that array.  Only problem is I can\u0026rsquo;t remember how to split a string by \u003ccode\u003e\u0026quot;\u0026quot;\u003c/code\u003e, I don\u0026rsquo;t think \u003ccode\u003eexplode( \u0026quot;\u0026quot;, $var )\u003c/code\u003e does the job.  So I quickly jump in and write the following code to test my concern.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-php\" data-lang=\"php\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u003c/span\u003e\u003cspan class=\"nx\"\u003ephp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eexplode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nv\"\u003e$c\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003earray_reverse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eecho\u003c/span\u003e \u003cspan class=\"nx\"\u003eimplode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$c\u003c/span\u003e \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e?\u0026gt;\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e# =\u0026gt; ERROR\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe reason for the error is because I\u0026rsquo;ve missed a semi colon off the end of line 2, to this I get the response, \u0026ldquo;well thats certainly not as nice as ruby.\u0026rdquo; Just because one little character is missing!\u003c/p\u003e\n\u003cp\u003eSo I fix the semi colon and run it again, now I get an error complaining about explode not being able to split by a missing delimiter (the empty string - \u003ccode\u003e\u0026quot;\u0026quot;\u003c/code\u003e)  So I go hunting through the \u003ca href=\"http://php.net/\"\u003ephp.net\u003c/a\u003e docs and find \u003ccode\u003estr_split()\u003c/code\u003e, which does exactly what I want it to.\u003c/p\u003e\n\u003cp\u003eIn replacing \u003ccode\u003eexplode()\u003c/code\u003e with \u003ccode\u003estr_split()\u003c/code\u003e and running it via the \u003ccode\u003ephp\u003c/code\u003e command line binary, I realise that I haven\u0026rsquo;t got any \u003ccode\u003e\\n\u003c/code\u003e (newlines) at the end of it, so it doesn\u0026rsquo;t display nicely in the terminal.  I thus update the script to the following and show her the result:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-php\" data-lang=\"php\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u003c/span\u003e\u003cspan class=\"nx\"\u003ephp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eexplode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nv\"\u003e$c\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003earray_reverse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eecho\u003c/span\u003e \u003cspan class=\"nx\"\u003eimplode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$c\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e?\u0026gt;\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e# =\u0026gt; \u0026#34;dlroW olleH\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd so she goes away seeing why I prefer Ruby to other languages for \u003cem\u003emost\u003c/em\u003e programming I do. There are things Ruby fails at (and don\u0026rsquo;t get me started on why rails isn\u0026rsquo;t going to replace php!) and other places where it succeeds very well.\u003c/p\u003e\n\u003cp\u003eBut each to their own, and my own favourite is Ruby!\u003c/p\u003e\n\u003ch3 id=\"update\"\u003eUpdate\u003c/h3\u003e\n\u003cp\u003eAs pointed out in the comments, if I had looked a bit further I would\u0026rsquo;ve found \u003ccode\u003estrrev()\u003c/code\u003e which does the same as the \u003ccode\u003ereverse\u003c/code\u003e method in Ruby.  So in fact the final code would be:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereverse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003evs\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-php\" data-lang=\"php\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u003c/span\u003e\u003cspan class=\"nx\"\u003ephp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eecho\u003c/span\u003e \u003cspan class=\"nx\"\u003estrrev\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e?\u0026gt;\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSo it turns out this was a bad way to show why I prefer Ruby to PHP code wise to mother, think I might have to just bite the bullet and write about why I prefer \u003ccode\u003eobject.method\u003c/code\u003e to \u003ccode\u003emethod( object )\u003c/code\u003e!\u003c/p\u003e\n"},{"title":"UK Parking","date_published":"2008-01-12T12:35:01Z","date_modified":"2008-01-12T12:35:01Z","id":"https://caiustheory.com/uk-parking/","url":"https://caiustheory.com/uk-parking/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo I just saw this in my twitter stream:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"http://twitter.com/peterc/statuses/590099072\"\u003epeterc\u003c/a\u003e: Warning UK drivers.. councils get powers on March 31st to give you parking fines for infractions they see on CCTV!\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eThis indeed sucks.  We\u0026rsquo;ll see how successful they are at ticketing people and whether anyone abuses the system\u0026hellip;\u003c/p\u003e\n"},{"title":"Published Pictures","date_published":"2008-01-05T16:08:39Z","date_modified":"2008-01-05T16:08:39Z","id":"https://caiustheory.com/published-pictures/","url":"https://caiustheory.com/published-pictures/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eInteresting email arrived in my inbox today.  I attended \u003ca href=\"http://barcamp.org/BarCampLeeds\"\u003eBarCamp Leeds\u003c/a\u003e in late 2007 and whilst I was there I happened to take some pictures (as I am apt to do these days.)\u003c/p\u003e\n\u003cp\u003eAnyway, I was rather happy to see that at least one picture isn\u0026rsquo;t just stagnating in my \u003ca href=\"http://flickr.com/photos/caius/\"\u003eflickr stream\u003c/a\u003e:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eHi Caius,\u003c/p\u003e\n\u003cp\u003eI am delighted to let you know that one of your photos with\u003cbr\u003e\na Creative Commons license has been selected for inclusion\u003cbr\u003e\nin the newly released fourth edition of our Schmap Leeds\u003cbr\u003e\nGuide:\u003c/p\u003e\n\u003cp\u003eTown Hall\u003cbr\u003e\n\u003ca href=\"http://www.schmap.com/leeds/sights_hydepark/p=39139/i=39139_10.jpg\"\u003ehttp://www.schmap.com/leeds/sights_hydepark/p=39139/i=39139_10.jpg\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"\"\u003eOriginal Picture on flickr\u003c/a\u003e\u003c/p\u003e\n"},{"title":"Twitter Statistics","date_published":"2008-01-02T07:15:00Z","date_modified":"2008-01-02T07:15:00Z","id":"https://caiustheory.com/twitter-statistics/","url":"https://caiustheory.com/twitter-statistics/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003e\u003ca href=\"http://flickr.com/photo_zoom.gne?id=2157771886\u0026amp;size=o\"\u003e\u003cimg src=\"http://farm3.static.flickr.com/2345/2157771886_9e41008ac2_m.jpg\" alt=\"Twitter Statistics for caius\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://dcortesi.com/2007/12/27/twitter-stats/\"\u003eJust\u003c/a\u003e \u003ca href=\"http://www.flickr.com/photos/gruber/2156085517/\"\u003ejoining\u003c/a\u003e \u003ca href=\"http://www.randsinrepose.com/archives/2008/01/01/year_in_twitter.html\"\u003ethe\u003c/a\u003e \u003ca href=\"http://kosmar.de/archives/2007/12/29/twitter-statistics/\"\u003eband\u003c/a\u003e \u003ca href=\"http://blog.nordquist.org/?p=2083\"\u003ewagon\u003c/a\u003e in posting up my twitter statistics.  The skew towards december being the month I twitter most in is because I enabled twitter sms, and ended up replying/updating a lot more often than I expected.\u003c/p\u003e\n"},{"title":"OS X User has a virus","date_published":"2008-01-02T01:15:15Z","date_modified":"2008-01-02T01:15:15Z","id":"https://caiustheory.com/os-x-user-has-a-virus/","url":"https://caiustheory.com/os-x-user-has-a-virus/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eNo not my computer, me.  I\u0026rsquo;ve managed to pickup an \u003c!-- raw HTML omitted --\u003eacute coryza\u003c!-- raw HTML omitted --\u003e virus.  It causes \u003ccode\u003e/dev/random\u003c/code\u003e to constantly pass data to \u003ccode\u003e/Volumes/nose\u003c/code\u003e.  I don\u0026rsquo;t think I got this from reading email, but I\u0026rsquo;m pretty sure it was a windows user who passed it to me.\u003c/p\u003e\n\u003cp\u003eIf I flush my partitions to disk every now and then I can get by with a clean \u003ccode\u003e/Volumes/nose\u003c/code\u003e for a while before \u003ccode\u003e/dev/random\u003c/code\u003e starts clogging it up again.\u003c/p\u003e\n"},{"title":"dSLR Quote","date_published":"2007-12-31T17:25:25Z","date_modified":"2007-12-31T17:25:25Z","id":"https://caiustheory.com/dslr-quote/","url":"https://caiustheory.com/dslr-quote/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eI was just talking to \u003ca href=\"http://keihatsu.org/\"\u003eNadim\u003c/a\u003e about him getting a new camera and then this happened:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eNadim: \u003ca href=\"http://cyraq.deviantart.com/art/Above-the-Clouds-72170767\"\u003ehttp://cyraq.deviantart.com/art/Above-the-Clouds-72170767\u003c/a\u003e\u003cbr\u003e\nNadim: Did you see that?\u003cbr\u003e\nNadim: Taken couple of weeks ago\u003cbr\u003e\nCaius: woa\u003cbr\u003e\nCaius: and you need a new camera because? :P\u003cbr\u003e\nNadim: I have wet dreams about DSLR\u0026rsquo;s\u003c/p\u003e\n\u003c/blockquote\u003e\n"},{"title":"Quote to see the new year in","date_published":"2007-12-31T17:11:11Z","date_modified":"2007-12-31T17:11:11Z","id":"https://caiustheory.com/quote-to-see-the-new-year-in/","url":"https://caiustheory.com/quote-to-see-the-new-year-in/","author":{"name":"Caius Durling"},"content_html":"\u003cp\u003eSo I\u0026rsquo;m a geek, as my friends are gradually realising more and more.  Anyway, I twitter a lot, and occasionally write quotes that need to be saved elsewhere.  And what generally makes tweets funnier are other people replying to them.\u003c/p\u003e\n\u003cp\u003eA little background information.  This had just taken place in #habari (a chat room.)\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026lt;h0bbel\u0026gt; ringmaster: i'll show you my winky\n* ringmaster runs.\n\u0026lt;h0bbel\u0026gt; as jmullan has seen it\n\u0026lt;h0bbel\u0026gt; i think\n\u0026lt;jmullan\u0026gt; no, I didn't look\n\u0026lt;ringmaster\u0026gt; 0_o\n\u0026lt;h0bbel\u0026gt; i was kinda drunk at the time\n\u0026lt;h0bbel\u0026gt; lol\n\u0026lt;jmullan\u0026gt; we were playing \u0026quot;lightsabers\u0026quot; while peeing\n\u0026lt;jmullan\u0026gt; not \u0026quot;swords\u0026quot;\n\u0026lt;jmullan\u0026gt; I had the yellow saber, h0bbel had the green one\n\u0026lt;jmullan\u0026gt; I'm not sure how that worked\n\u0026lt;h0bbel\u0026gt; you said you didn't look!\n\u0026lt;h0bbel\u0026gt; How did you know it was green?\n\u0026lt;jmullan\u0026gt; I just saw the beam, not the handle, if you know what I'm saying\n\u0026lt;h0bbel\u0026gt; ehehe\n\u0026lt;Caius\u0026gt; xD\n\u0026lt;ringmaster\u0026gt; This is all somewhat disturbing.\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eSo in order\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"http://twitter.com/caius/statuses/549893582\"\u003eme\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eman, #habari just wouldn\u0026rsquo;t be the same without the semi-homosexual discussions every now and then\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"http://twitter.com/caius/statuses/549893582\"\u003eme\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eFormatting some SQL queries for a fellow dev\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"http://twitter.com/h0bbel/statuses/549895502\"\u003eh0bbel\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"http://twitter.com/caius/statuses/549893582\"\u003ehttp://twitter.com/caius/st\u0026hellip;\u003c/a\u003e is realy quoteworthy ;-)\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"http://twitter.com/tmertz/statuses/549899752\"\u003eMertz\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e@\u003ca href=\"http://twitter.com/caius\"\u003ecaius\u003c/a\u003e, wtf are you doing? put down the computer, take five steps back and go find a new years celebration.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"http://twitter.com/caius/statuses/549903292\"\u003eme\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e@\u003ca href=\"http://twitter.com/tmertz\"\u003etmertz\u003c/a\u003e just hanging in #habari whilst editing some SQL queries @\u003ca href=\"http://twitter.com/h0bbel\"\u003eh0bbel\u003c/a\u003e wrote waiting for 1900GMT to go round my mates and get drunk\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAnd on the back of that, I\u0026rsquo;d just like to say have a very merry new years eve and just follow your desires onwards in life!\u003c/p\u003e\n"}]}