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

<channel>
	<title>Jotlab</title>
	<atom:link href="http://www.jotlab.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.jotlab.com</link>
	<description>Just another WordPress site</description>
	<lastBuildDate>Fri, 22 Mar 2013 01:15:26 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Garmin 2 Strava Sync Ruby Script</title>
		<link>http://www.jotlab.com/2013/garmin-2-strava-sync-ruby-script</link>
		<comments>http://www.jotlab.com/2013/garmin-2-strava-sync-ruby-script#comments</comments>
		<pubDate>Sun, 24 Feb 2013 06:36:30 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[Cycling]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[cycling]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[stats]]></category>
		<category><![CDATA[strava]]></category>
		<category><![CDATA[sync]]></category>

		<guid isPermaLink="false">http://new.jotlab.com/?p=4861</guid>
		<description><![CDATA[Having had issues with Garmin Communicator in the recent days. Plus being lazy of going to Strava, clicking on all the links, tabs and waiting for uploads. I figure I would make a ruby script that I can easily run when I get to my... <span><a href="http://www.jotlab.com/2013/garmin-2-strava-sync-ruby-script" title="Garmin 2 Strava Sync Ruby Script" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>Having had issues with Garmin Communicator in the recent days. Plus being lazy of going to Strava, clicking on all the links, tabs and waiting for uploads. I figure I would make a ruby script that I can easily run when I get to my desk or home that grabs all new activities that haven&#8217;t yet been sync&#8217;d and push them to Strava.</p>
<p>If you&#8217;re on mac you can run a LaunchDaemon that will sniff out the volume when it is connected and auto sync. I haven&#8217;t got up to that just yet but here is the script <img src='http://www.jotlab.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
<pre class="brush: ruby; title: ; notranslate">require 'rest-client'
require 'highline/import'
require 'json'
require 'time'

@activityCount = 0
@uploadedCount = 0
@garminPath = &quot;/Volumes/GARMIN/Garmin/Activities&quot;

def startup()
    email = ask(&quot;Email: &quot;)
    password = ask(&quot;Password: &quot;) { |p| p.echo = false }

    if email &amp;&amp; password
        puts &quot;Authenticating with Strava...&quot;
        begin
            response = RestClient.post &quot;https://www.strava.com/api/v2/authentication/login&quot;, { email: email, password: password }.to_json, :content_type =&gt; :json, :accept =&gt; :json
        rescue Exception =&gt; e
            puts &quot;Uh oh. It failed. #{e.message}&quot;
        end

        if response.code == 200
            user = JSON.parse(response.body)
            puts &quot;Your session token is: #{user[&quot;token&quot;]}&quot;
            
            if !File.exists?(&quot;#{@garminPath}/.stravaTrakt&quot;)
                File.open(&quot;#{@garminPath}/.stravaTrakt&quot;, &quot;w&quot;)
            end

            dotTrakr = File.open(&quot;#{@garminPath}/.stravaTrakt&quot;, &quot;r+&quot;)
            
            timeLastSaved = dotTrakr.read
            if timeLastSaved.length &gt; 0
                timeLastSaved = Time.parse(timeLastSaved)
            end

            if !timeLastSaved.is_a?(Time)
                files = Dir.glob(&quot;#{@garminPath}/*.fit&quot;)
                @activityCount = files.length
                files.each { |f| uploadFile(user, f) }
            else
                files = Dir.glob(&quot;#{@garminPath}/*.fit&quot;).
                    select{|f| File.mtime(f) &gt; timeLastSaved }
                @activityCount = files.length
                files.each{|f| uploadFile(user, f) }
            end
            dotTrakr.close

            if @activityCount == 0
                puts &quot;No activities to sync&quot;
            elsif @uploadedCount == @activityCount
                timeLastSaved = Time.now
                dotTrakr = File.open(&quot;#{@garminPath}/.stravaTrakt&quot;, &quot;w+&quot;)
                dotTrakr.write(timeLastSaved).close
                puts &quot;#{@activityCount} activities have been uploaded. Good work!&quot;
            else
                puts &quot;Some activities were skipped. Look into this&quot;
            end
        end

    end
end

def uploadFile(user, activity)
    puts &quot;Syncing Activity - #{File.basename(activity)} to Strava&quot;
    payload = { token: user[&quot;token&quot;], data: File.new(activity, 'rb').read, type: &quot;fit&quot;}

    begin
        response = RestClient.post &quot;http://www.strava.com/api/v2/upload&quot;, payload
        if response.code == 200
            body = JSON.parse(response.body)
            puts &quot;Great Success! Upload ID: #{body[&quot;upload_id&quot;]}&quot;
            @uploadedCount = @uploadedCount + 1
        end
    rescue Exception =&gt; e
        puts &quot;Uh oh, something went wrong uploading the activity: #{e.message}&quot;
    end

end

startup()</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2013/garmin-2-strava-sync-ruby-script/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Syncd: Remembering What We Delete On Remotes</title>
		<link>http://www.jotlab.com/2012/syncd-remembering-what-we-delete-on-remotes</link>
		<comments>http://www.jotlab.com/2012/syncd-remembering-what-we-delete-on-remotes#comments</comments>
		<pubDate>Fri, 14 Dec 2012 22:47:33 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[sync]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4802</guid>
		<description><![CDATA[A friend of mine doesn&#8217;t have internet. Yep, they exist. But they do love movies, music etc. I offered to open up my movies and music to him (legit ones only of course) but ran into an issue quite quickly. The problem with sharing data... <span><a href="http://www.jotlab.com/2012/syncd-remembering-what-we-delete-on-remotes" title="Syncd: Remembering What We Delete On Remotes" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>A friend of mine doesn&#8217;t have internet. Yep, they exist. But they do love movies, music etc. I offered to open up my movies and music to him (legit ones only of course) but ran into an issue quite quickly.</p>
<p>The problem with sharing data is that you need to have enough space on the destination media to support all of the data on the host machine; thanks to my failing maths, my friend did not. His HDD filled up to the brim, and missed out newer stuffs. Instead of taking it back and trying to return it, I decided to be a bit more resourceful and takle the issue at hand.</p>
<h2>Syncd.rb</h2>
<p>Syncd is a simple Ruby script that remembers what was copied onto the destination device/folder and makes sure that if that file goes subsequently missing (like deleting it because the movie wasn&#8217;t of their taste) it won&#8217;t be re-added upon next sync. So what this means is, my friend gets movies, and deletes all the ones he doesn&#8217;t like, which frees up space for new ones that he didn&#8217;t have previously.</p>
<p>But how does it work? Pretty simple. On the host machine we litter the folders to be sync&#8217;d with indexed tracking crumbs, by default .syncd. These files contain a list of files previously sync&#8217;d. Upon each sync the script checks the host .syncd crumb file to see if a file was previously sync&#8217;d, if it does check if it still exists on the host, if it doesn&#8217;t, don&#8217;t sync, it&#8217;s been deleted. If it does still exist check if the file is still the same and not modified, if it differs, it needs a resync.</p>
<h2>Gimme Script Already</h2>
<pre class="brush: ruby; title: ; notranslate">require 'fileutils'
require 'pathname'
require 'digest'
require 'colorize'

class Syncd

def initialize
@dir = '/Volumes/MUSIC'
@remote = '/Users/richard/mew'
@crumbName = '.syncd'
end

def sync(dir = nil)
dir = @dir unless dir
Dir.foreach(dir) { |f|
path = dir + '/' + f
next if (f == '..' || f == '.')
if File.directory?(path)
self.sync(path)
else
self.syncFile(path)
end
}
end

@private
def previouslySyncd(file)
filename = File.basename(file)
syncdCrumb = File.dirname(file) + &quot;/#{@crumbName}&quot;
if !File.exists?(syncdCrumb)
FileUtils.touch(syncdCrumb)
end

if File.readlines(syncdCrumb).grep(filename).size == 0
open(syncdCrumb, 'a') { |f|
f.puts File.basename(file)
}
return false
else
return true
end
end

def fileIsIdentical(file, remoteFile)
return false unless File.exists?(remoteFile)
return FileUtils.compare_file(file, remoteFile)
end

def syncFile(file)
remoteFile = @remote + file[@dir.size..file.size]
print &quot;Syncing #{file}... &quot;.light_green
if !previouslySyncd(file) &amp;amp;&amp;amp; !fileIsIdentical(file, remoteFile)
begin
Dir.mkdir(File.dirname(remoteFile)) unless File.exists?(File.dirname(remoteFile))
FileUtils.cp(file, remoteFile)
puts &quot;Success!&quot;.green
rescue Exception =&amp;gt; e
puts &quot;Error: #{e}&quot;.red
end
else
puts &quot;Skipped, no need to sync&quot;.blue
end
end

end

syncd = Syncd.new()
syncd.sync()</pre>
<p>You may need to install the gems like colorize, simply do:</p>
<pre class="brush: bash; title: ; notranslate">gem install colorize</pre>
<p>Also see the defaults in the instance vars. Change them to suit your needs <img src='http://www.jotlab.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h2>But why not use rsync?</h2>
<p>Rsync doesn&#8217;t have a mechanism for keeping track of what it has previously sync&#8217;d and what has been deleted.</p>
<h2>TODO:</h2>
<ul>
<li>Optimise the identical hash checks (they slow)</li>
<li>Support multiple input sources</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2012/syncd-remembering-what-we-delete-on-remotes/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iOS Keyboard Style Reference</title>
		<link>http://www.jotlab.com/2012/ios-keyboard-style-reference</link>
		<comments>http://www.jotlab.com/2012/ios-keyboard-style-reference#comments</comments>
		<pubDate>Sat, 08 Sep 2012 23:42:56 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[apple]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[ipad]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4777</guid>
		<description><![CDATA[Just a simple small post showcasing the various keyboard formats along with the associated properties. To be used for future reference to easily see the formats: UIKeyboardTypeDefault Use the default keyboard for the current input method. UIKeyboardTypeASCIICapable Use a keyboard that displays standard ASCII characters.... <span><a href="http://www.jotlab.com/2012/ios-keyboard-style-reference" title="iOS Keyboard Style Reference" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>Just a simple small post showcasing the various keyboard formats along with the associated properties. To be used for future reference to easily see the formats:</p>
<h3>UIKeyboardTypeDefault</h3>
<p>Use the default keyboard for the current input method.</p>
<p><img src="http://www.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.25.18-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.25.18 AM" width="396" height="744" class="aligncenter size-full wp-image-4778" /></p>
<h3>UIKeyboardTypeASCIICapable</h3>
<p>Use a keyboard that displays standard ASCII characters.</p>
<p><img src="http://www.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.28.45-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.28.45 AM" width="396" height="744" class="aligncenter size-full wp-image-4781" /></p>
<h3>UIKeyboardTypeNumbersAndPunctuation</h3>
<p>Use the numbers and punctuation keyboard.</p>
<p><img src="http://www.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.30.22-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.30.22 AM" width="396" height="744" class="aligncenter size-full wp-image-4782" /></p>
<h3>UIKeyboardTypeURL</h3>
<p>Use a keyboard optimized for URL entry. This type features “.”, “/”, and “.com” prominently.</p>
<p><img src="http://www.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.31.42-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.31.42 AM" width="396" height="744" class="aligncenter size-full wp-image-4783" /></p>
<h3>UIKeyboardTypeNumberPad</h3>
<p>Use a numeric keypad designed for PIN entry. This type features the numbers 0 through 9 prominently. This keyboard type does not support auto-capitalization.</p>
<p><img src="http://www.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.32.50-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.32.50 AM" width="396" height="744" class="aligncenter size-full wp-image-4784" /></p>
<h3>UIKeyboardTypePhonePad</h3>
<p>Use a keypad designed for entering telephone numbers. This type features the numbers 0 through 9 and the “*” and “#” characters prominently. This keyboard type does not support auto-capitalization.</p>
<p><img src="http://new.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.33.43-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.33.43 AM" width="396" height="744" class="aligncenter size-full wp-image-4785" /></p>
<h3>UIKeyboardTypeNamePhonePad</h3>
<p>Use a keypad designed for entering a person’s name or phone number. This keyboard type does not support auto-capitalization.</p>
<p><img src="http://www.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.35.12-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.35.12 AM" width="396" height="744" class="aligncenter size-full wp-image-4786" /></p>
<h3>UIKeyboardTypeEmailAddress</h3>
<p>Use a keyboard optimized for specifying email addresses. This type features the “@”, “.” and space characters prominently.</p>
<p><img src="http://new.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.36.49-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.36.49 AM" width="396" height="744" class="aligncenter size-full wp-image-4788" /></p>
<h3>UIKeyboardTypeDecimalPad</h3>
<p>Use a keyboard with numbers and a decimal point.</p>
<p><img src="http://new.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.38.16-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.38.16 AM" width="396" height="744" class="aligncenter size-full wp-image-4789" /></p>
<h3>UIKeyboardTypeTwitter</h3>
<p>Use a keyboard optimized for twitter text entry, with easy access to the @ and # characters.</p>
<p><img src="http://new.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.39.26-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.39.26 AM" width="396" height="744" class="aligncenter size-full wp-image-4790" /></p>
<h3>UIKeyboardTypeAlphabet</h3>
<p>Deprecated.</p>
<p><img src="http://new.jotlab.com/wp-content/uploads/2012/09/Screen-Shot-2012-09-09-at-9.40.15-AM.png" alt="" title="Screen Shot 2012-09-09 at 9.40.15 AM" width="396" height="744" class="aligncenter size-full wp-image-4791" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2012/ios-keyboard-style-reference/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>On My Tv: With Trakt.tv</title>
		<link>http://www.jotlab.com/2012/on-my-tv-with-trakt-tv</link>
		<comments>http://www.jotlab.com/2012/on-my-tv-with-trakt-tv#comments</comments>
		<pubDate>Sat, 28 Jul 2012 05:31:33 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[deb]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[trakt]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4764</guid>
		<description><![CDATA[By now you&#8217;ve probably discovered I&#8217;m a big fan of trakt.tv, well scrobbling in general. I wan&#8217;t a way to show what I&#8217;m watching on my own blog in a way that isn&#8217;t the preformatting jpg&#8217;s trakt.tv provide (which is pretty cool in it&#8217;s own... <span><a href="http://www.jotlab.com/2012/on-my-tv-with-trakt-tv" title="On My Tv: With Trakt.tv" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>By now you&#8217;ve probably discovered I&#8217;m a big fan of trakt.tv, well scrobbling in general. I wan&#8217;t a way to show what I&#8217;m watching on my own blog in a way that isn&#8217;t the preformatting jpg&#8217;s trakt.tv provide (which is pretty cool in it&#8217;s own right).</p>
<p>So with my trakt.tv api key I decided to write up a quick ruby script to show what&#8217;s currently on my tv:</p>
<pre class="brush: ruby; title: ; notranslate">require &quot;net/http&quot;
require &quot;uri&quot;
require 'json'
require 'yaml'

uri = URI.parse(&quot;http://api.trakt.tv/user/watching.json/your_api_key/your_username&quot;)
response = Net::HTTP.get_response(uri)
result = JSON.parse response.body

if result.is_a?(Hash)
	
	if result[&quot;show&quot;]
		title = &quot;&lt;div style=&quot;padding-left: 20px;&quot;&gt;&lt;h3&gt;ON MY TV&lt;/h3&gt;&lt;h5 style=&quot;margin-bottom: 10px;&quot;&gt;#{result['show']['title']} - #{result['episode']['title']} (s#{result['episode']['season']}e#{result['episode']['number']})&lt;/h5&gt;&quot;
		poster = result['show']['images']['poster'].gsub('.jpg', '-138.jpg')
		title &lt;&lt; &quot;&lt;img src=&quot;#{poster}&quot; /&gt;&lt;/div&gt;&quot;
	elsif result[&quot;movie&quot;]
		title = &quot;&lt;div style=&quot;padding-left: 20px;&quot;&gt;&lt;h3&gt;ON MY TV&lt;/h3&gt;&lt;h5 style=&quot;margin-bottom: 10px;&quot;&gt;#{result['movie']['title']} (#{result['movie']['year']})&lt;/h5&gt;&quot;
		poster = result['movie']['images']['poster'].gsub('.jpg', '-138.jpg')
		title &lt;&lt; &quot;&lt;img src=&quot;#{poster}&quot; /&gt;&lt;/div&gt;&quot;
	end

	trakt = File.open(&quot;/path/to/text_file/playing.txt&quot;, &quot;w&quot;)
	trakt.write(title)
	trakt.close

end</pre>
<p>Three things to note here. You will need to update your api-key, username and the path to where you want the text file to be written.</p>
<p>Next up I poll trakt&#8217;s api every 5mins via:</p>
<pre class="brush: bash; title: ; notranslate">crontab -e
*/5 * * * * ruby trakt.rb</pre>
<p>Which updates the playlist.txt file. I then bring that into my blog with a simple:</p>
<pre class="brush: php; title: ; notranslate">echo file_get_contents('playlist.txt');</pre>
<p>Let me know if you use this so I can checkout some new things to watch!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2012/on-my-tv-with-trakt-tv/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Uploading with Rails 3.1, CarrierWave, MediaInfo, Single Table Inheritance and Polymorphism</title>
		<link>http://www.jotlab.com/2012/uploading-with-rails-3-1-carrierwave-mediainfo-single-table-inheritance-and-polymorphism</link>
		<comments>http://www.jotlab.com/2012/uploading-with-rails-3-1-carrierwave-mediainfo-single-table-inheritance-and-polymorphism#comments</comments>
		<pubDate>Mon, 16 Jan 2012 11:57:37 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[3.1]]></category>
		<category><![CDATA[carrierwave]]></category>
		<category><![CDATA[media info]]></category>
		<category><![CDATA[polymorphic]]></category>
		<category><![CDATA[polymorphism]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[simple_form]]></category>
		<category><![CDATA[single table inheritance]]></category>
		<category><![CDATA[sti]]></category>
		<category><![CDATA[uploads]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4747</guid>
		<description><![CDATA[Starting a new Ruby on Rails project I decided to try out the much talked about CarrierWave gem. It makes uploading easy and keeps uploader config separated from your models. Clean! I decided to write this little tutorial as to how to get CarrierWave working... <span><a href="http://www.jotlab.com/2012/uploading-with-rails-3-1-carrierwave-mediainfo-single-table-inheritance-and-polymorphism" title="Uploading with Rails 3.1, CarrierWave, MediaInfo, Single Table Inheritance and Polymorphism" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>Starting a new Ruby on Rails project I decided to try out the much talked about CarrierWave gem. It makes uploading easy and keeps uploader config separated from your models. Clean! I decided to write this little tutorial as to how to get CarrierWave working with Single Table Inheritance (STI) and a polymorphic table, using dynamic save paths, extracting and saving metadata of the media on save&#8230;. *breathe in, breathe out*</p>
<h2>Stop Schema Time!</h2>
<p>First up let&#8217;s talk about the database schema. If you&#8217;re new to the concept of single table inheritance and polymorphism then I hope I can explain how I&#8217;ve structured my schema to handle, hopefully, a myriad of media design solutions. We&#8217;ll use the models I will be using later on in this tutorial to set up this discussion:</p>
<ul>
<li>A user <em>has_many</em> shows</li>
<li>A show <em>has_many</em> episodes</li>
<li>An episode <em>has_many</em> videos</li>
<li>An episode <em>has_many</em> audio files</li>
<li>An episode <em>has_many</em> images</li>
</ul>
<p>Now immediately you might be thinking of hitting up <em>rails g migration etc</em> and generating a videos, audio and images table, all with an episode_id field. Hold your horses. What happens when I say that a show can also have many images, you would then need to make a seperate table for shows_images, and it can keep going and going.</p>
<h2>Polymorphism!</h2>
<p>Introducing polymorphism! A polymorphic model is a model that wraps over itself, is independent on a parent model, and cares not about your ids. It belongs to itself and no one else, its it&#8217;s own woman. But how do you reference back to your models. Well rails handles this issue quite well, it stores the referencing model in a #{alias}_type field, and the according owner id in its #{alias}_id field. Going forward an example polymorphic table would be:</p>
<pre class="brush: sql; title: ; notranslate">CREATE TABLE `assets` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
	`type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
	`filename` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
	`checksum` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
	`path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
	`filesize` int(11) DEFAULT NULL,
	`width` int(11) DEFAULT NULL,
	`height` int(11) DEFAULT NULL,
	`duration` int(11) DEFAULT NULL,
	`bit_rate` int(11) DEFAULT NULL,
	`assetable_id` int(11) DEFAULT NULL,
	`assetable_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
	`created_at` datetime DEFAULT NULL,
	`updated_at` datetime DEFAULT NULL,
	PRIMARY KEY (`id`)
)</pre>
<p>So it&#8217;s SQL, get over it. So now if it&#8217;s an image, or a video, or an audio file, or any file, they get stored here. Now you don&#8217;t have to store all your media metadata and associations here, but it can save on tables and simplifies asset handling. Now single table inheritance comes into play by putting a model inbetween the polymorphic Asset model and the owner model. So instead of Episode has_many Asset (a bit weird), we can simply do Episode has_many Videos where Videos extends Asset. A bit much to get your head around, lets make some migrations, make some models and see how the schema pans out!</p>
<h2>Rails Time</h2>
<p>TO get started cd to the chose place you do your business in and run:</p>
<pre class="brush: bash; title: ; notranslate">rails new test_app</pre>
<p>Fill our your condit/database.yml config so you can connect to the db. Once created we may as well add the required gems for this walkthrough. Open up your Gemfile and add in:</p>
<pre class="brush: ruby; title: ; notranslate">gem 'mysql2'
gem 'simple_form'
gem 'carrierwave'
gem 'rmagick'
gem 'friendly_id'
gem 'uuidtools'
gem 'mediainfo'</pre>
<p>Save and then in your command line run <em>bundle install</em>, good times. Now let&#8217;s start with our migrations, we&#8217;ll skip the user side of things for the time being as I don&#8217;t really want to include authentication in this tutorial. So Let&#8217;s start with the Show model. In your command line run:</p>
<pre class="brush: bash; title: ; notranslate">rails g model Show title:string slug:string</pre>
<p>Now the Episode Model:</p>
<pre class="brush: bash; title: ; notranslate">rails g model Episode episode_id:integer title:string slug:string</pre>
<p>For the asset table we&#8217;ll just make a blank model with no fields. We will modify the asset migration manually. Just so we can see how to reference, the references&#8230;</p>
<pre class="brush: bash; title: ; notranslate">rails g model Asset</pre>
<p>After that, open the create_assets.rb migration file in db/migrate/ and look at the change method. You should see something stock standard:</p>
<pre class="brush: ruby; title: ; notranslate">class CreateAssets &lt; ActiveRecord::Migration
  def change
    create_table :assets do |t|

      t.timestamps
    end
  end
end</pre>
<p>Cool story hansel&#8230; But this does nothing! No polymorphism, in fact just a lot of tumbleweed. Lets paste this in instead of what is there and then discuss</p>
<pre class="brush: ruby; title: ; notranslate">class CreateAssets &lt; ActiveRecord::Migration
  def change
    create_table :assets do |t|
    	t.string :type
    	t.string :filename
    	t.string :checksum
    	t.string :path
    	t.integer :filesize
    	t.integer :width
    	t.integer :height
    	t.integer :duration
    	t.integer :bit_rate

    	t.references :assetable, :polymorphic =&gt; true
      t.timestamps
    end
  end
end</pre>
<p>Now the first 9 fields are all specific to the media&#8217;s meta data. We will be populating them a bit later. The most important line is	<em>t.references :assetable, :polymorphic => true</em>. Do not forget this one. It sets up the polymorphism, assetable makes the table self referential, and polymorphic true handles the _type field creation, so you can see what model it belongs to. Boom! let&#8217;s rake</p>
<pre class="brush: bash; title: ; notranslate">bundle exec rake db:create
bundle exec rake db:migrate</pre>
<p>Hopefully all went well and your tables are created!</p>
<h2>Scaffolding</h2>
<p>Let&#8217;s get our views and controllers done and dusted so we can get into the cool stuff</p>
<pre class="brush: bash; title: ; notranslate">rails g scaffold shows --skip-migration
rails g scaffold episodes --skip-migration</pre>
<p>Overwrite the tests, we don&#8217;t have time to do them today. With that done lets go straight to setting up our associations.</p>
<h2>Assetable?</h2>
<p>Open up our show model in models/show.rb We will set up the association it requires, and also some added magic to get slugs going.</p>
<pre class="brush: ruby; title: ; notranslate">class Show &lt; ActiveRecord::Base

  #Associations
  #belongs_to :user
  has_many :episodes

  #Slugs
  extend FriendlyId
  friendly_id :title, use: :slugged

end</pre>
<p>You can see this is pretty straight forward with the association to episodes. friendly_id? wtf? It&#8217;s an awesome gem that turns whatever you want into a slug. Here we are turning the title field into a slug, stored by default into the slug field. We have that covered in our migrations already</p>
<p>Next up let&#8217;s do the more complex one, our episode model:</p>
<pre class="brush: ruby; title: ; notranslate">class Episode &lt; ActiveRecord::Base

	belongs_to :show
  has_many :videos, :as =&gt; :assetable
  accepts_nested_attributes_for :videos, :reject_if =&gt; proc { |v| v[:filename].blank? }

  before_save :set_asset_path

	#Slugs
  extend FriendlyId
  friendly_id :title, use: :slugged

  validates :title, :presence =&gt; true, :length =&gt; {:within =&gt; 5..40}

  protected
  def set_asset_path
    self.videos.each do |e|
      e.path = &quot;uploads/#{self.show.slug}/#{self.slug}&quot;
    end
  end

end</pre>
<p>Okay let&#8217;s step through this line by line (we still haven&#8217;t made our STI&#8217;s yet, we&#8217;ll get to that soon). Firstly we set up our association back to show, it will come in handy. Next up we make a <em>has_many :videos</em> however, it acts as assetable. This tells episode to treat the association as the destination table being polymorphic. So when it looks up it will use the asset_type and asset_id fields in unison to return the relevant record. This doesn&#8217;t make the table polymorphic, we need to hook that up in the Asset model also</p>
<p>The <em>accepts_nested_attributes_for</em> property allows us to save videos through the episode model directly to it&#8217;s destination table, in this case asset table (we need to make the link, patience). :reject_if is a validation that will ignore the file if it has a null filename. This stops people from submitting the form with a file, and storing a null record into the asset table, bit annoying but very easy to stop.</p>
<p>The before save call runs a special method I&#8217;ve made which gives us power to save the file into a dynamic location on the server. In this case I store it in /uploads/show-slug-name/episode-slug/filename.mp3. This is also saved in the database, for your pleasure. Besides that there is yet another friendly slug, to make a slug for our episode and then a basic validation rule on the title. Boom!</p>
<h2>ASSETS Model</h2>
<pre class="brush: ruby; title: ; notranslate">class Asset &lt; ActiveRecord::Base
  belongs_to :assetable, :polymorphic =&gt; true
  before_save :store_metadata

  private
  def store_metadata
  	if self.filename.present? &amp;amp;amp;amp;&amp;amp;amp;amp; self.changed?
  	  file_path = &quot;#{Rails.root}/public/#{self.filename}&quot;
  		file = Mediainfo.new file_path
  		if file.image?
	  		image_meta(file)
	  	elsif file.video?
	  		video_meta(file, file_path)
	  	elsif file.audio?
				audio_meta(file)
	  	end
  	end
  end

  def video_meta(file, file_path)
    self.filesize = file.size
    self.bit_rate = file.video.bit_rate
    self.duration = file.video.duration
    self.checksum = ::Digest::MD5.file(file_path).hexdigest
    self.width = file.video.width
    self.height = file.video.height
  end

end</pre>
<p>Now the majority of this model is metadata extraction, we will get to that a bit later. For now we&#8217;ll look at line 2, belongs_to. This sets up the association to it self, stating that it&#8217;s also polymorphic. This ties in all the love we needed for this model! But our uploaders are not in just yet. We will need to go back and fill in the gaps. And by gaps lets add in our single table inheritance models.</p>
<h2>STI <3</h2>
<p>Firstly let&#8217;s create this model. Make a new folder in app/models and call it episode. Inside it let&#8217;s make the file video.rb What is this? This is the middle earth for our association. Rails automatically sets this join up so it seems, so to add this middle point in let&#8217;s add some code to get a deeper understanding as to just what is going on!</p>
<pre class="brush: ruby; title: ; notranslate">class Episode::Video &lt; Asset
  mount_uploader :filename, VideoUploader
  attr_accessible :filename, :assets
end</pre>
<p>A key point to this file is line 1. This sets up the middle association, Episode::Video, which is the classname, directly extends Asset! Finally! So working backwards. A video is an asset that belongs to an Episode that belongs to a Show! Line 2 sets up the CarrierWave uploader, which we will create shortly. In order to save to the asset table, we need to tell Rails that the attributes to :assets are also accessible (this had me stumped for a bit).</p>
<p>Coming shortly&#8230; Carrierwave uploaders, simple_form, and the media info gem love!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2012/uploading-with-rails-3-1-carrierwave-mediainfo-single-table-inheritance-and-polymorphism/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>DarthFader: A jQuery Carousel Plugin with Force</title>
		<link>http://www.jotlab.com/2012/darthfader-a-jquery-carousel-plugin-with-force</link>
		<comments>http://www.jotlab.com/2012/darthfader-a-jquery-carousel-plugin-with-force#comments</comments>
		<pubDate>Thu, 12 Jan 2012 09:07:10 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[star wars]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4741</guid>
		<description><![CDATA[Working with a fair few jQuery image sliders I started to get frustrated with the quirks and rules of some of them. Requiring a paragraph tag to simply add in a caption, only order lists supported, not my cup of tea. So for a new... <span><a href="http://www.jotlab.com/2012/darthfader-a-jquery-carousel-plugin-with-force" title="DarthFader: A jQuery Carousel Plugin with Force" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>Working with a fair few jQuery image sliders I started to get frustrated with the quirks and rules of some of them. Requiring a paragraph tag to simply add in a caption, only order lists supported, not my cup of tea. So for a new project I decided to write my own plugin. The project required cross fading, so instantly the plugin was crowned darthFader.</p>
<p>Starting off this plugin is currently in it&#8217;s infancy. It supports crossfading of whatever elements you want, provided they are wrapped in a container. So if you have a bunch of Div&#8217;s, IMG&#8217;s, LI&#8217;s, or hopes and dreams layered behind one another, the darthFader has you covered.</p>
<p>It also allows you to set up a nav, with forward and back buttons, and dots to replicate how many items in the carousel, of which are clickable to jump you to the item you&#8217;ve chosen.</p>
<p>Download the jQuery plugin via:</p>
<pre class="brush: bash; title: ; notranslate">git clone https://github.com/voidet/jQuery.darthFader /path/to/my/awesome/project</pre>
<p>Sites using the force:</p>
<p>http://www.langfordsjewellers.com/</p>
<p>http://www.goplacesbrisbane.com/</p>
<p>http://www.honeybs.tv/</p>
<p>This will save the darthFader plugin ready for you to include in your project. From there you can attach it to your carousel elements by:</p>
<pre class="brush: jscript; title: ; notranslate">$('#carousel-element').darthFader()</pre>
<p>Currently there are a few options you can override. The carousel nav element and the pause duration. More will come. I plan on adding a heap more animation styles and configuration options to give more flexibility to the plugin.</p>
<p>However an example on how to override the current options would be:</p>
<pre class="brush: jscript; title: ; notranslate">$('#carousel-element').darthFader({&quot;nav&quot;: '#this-is-my-nav-element', 'speed': 5000})</pre>
<p>Anyhow let me know if you have any additional features you would like to see included and I will happily consider <img src='http://www.jotlab.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2012/darthfader-a-jquery-carousel-plugin-with-force/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Plex &amp; Trakt: A Scrobbling Love Story</title>
		<link>http://www.jotlab.com/2012/plex-trakt-a-scrobbling-love-story</link>
		<comments>http://www.jotlab.com/2012/plex-trakt-a-scrobbling-love-story#comments</comments>
		<pubDate>Tue, 03 Jan 2012 10:07:29 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[apple]]></category>
		<category><![CDATA[appletv]]></category>
		<category><![CDATA[appletv2]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[jailbreak]]></category>
		<category><![CDATA[launchtl]]></category>
		<category><![CDATA[plex]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[scrobble]]></category>
		<category><![CDATA[trakt]]></category>
		<category><![CDATA[tv]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4732</guid>
		<description><![CDATA[I love my AppleTV2. Not because you can purchase movies and TV shows online, wait a heap of time for them to buffer, then watch them disappear from your library the next day. Nope. But because the little black box can be jailbroken and turned... <span><a href="http://www.jotlab.com/2012/plex-trakt-a-scrobbling-love-story" title="Plex &#038; Trakt: A Scrobbling Love Story" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>I love my AppleTV2. Not because you can purchase movies and TV shows online, wait a heap of time for them to buffer, then watch them disappear from your library the next day. Nope. But because the little black box can be jailbroken and turned into the beast it should of been.</p>
<p>Now this isn&#8217;t a jailbreak tutorial, unlike the iPhone tuts I&#8217;ve written in the past. Otherwise this post would be obsolete before I push the publish button. Instead I&#8217;m going to show you how to pair up Plex Media Center&#8217;s awesomeness with Trakt&#8217;s wickedness so you can follow what you&#8217;ve watched, get recommendations and follow trends, all automatically through a scrobbling script.</p>
<p>First of all I will assume you have Plex Media Centre installed on the machine that you are going to scrobble from (this post isn&#8217;t at all specific to AppleTV, as the scrobble script analyses Plex Media Centre&#8217;s log files). I will also assume you have the libraries set up on your media centre and that you have happily enjoyed viewing content from the server before. Now lastly you will need a Trakt.tv account. They&#8217;re free, and damn simple to get so head over to Trakt.tv and set up an account.</p>
<p>Now with all the fluff out of the way lets get started. Ensure the media server is set up and running. Next we&#8217;ll clone down the scrobbling script created by Tester22, who seems to be all about the automated torrent and workflow scripts for mac. Open up Terminal.app and:</p>
<pre class="brush: bash; title: ; notranslate">cd ~/Library/
git clone https://github.com/voidet/Plex-Trakt-Scrobbler.git plextrakt
cd plextrakt</pre>
<p>What did we just do here? Well we copied down a running git repository from tester22 of which we will work with now. Next up we need to enter our Trakt.tv username and password:</p>
<pre class="brush: bash; title: ; notranslate">mv config_sample.ini config.ini
vi config.ini</pre>
<p>We moved the sample config.ini to the appropriate config.ini file the scrobble script is expecting. You should now see something like:</p>
<pre class="brush: python; title: ; notranslate">[Trakt]
username = your_username
password = your_password

[Optional]
log_path =</pre>
<p>We will ignore the log_path for now, but change your_username and your_password to the trait username and password you signed up with. You are using the text editor vim, so if you need a hand look up the shortcuts. But for a quick help, press i to change the cursor to insert text, escape to get pointer back, and shift + zz to save and close. That should be all you need.</p>
<p>With that done we need to test the script. Back in terminal (with vim closed and your config all sorted) run:</p>
<pre class="brush: bash; title: ; notranslate">python script.py</pre>
<p>Now when I first ran this I had some issues, namely with element_tree and simple_json. Quite simply, they weren&#8217;t installed. So if you have these issues, lets install them! Back in terminal:</p>
<pre class="brush: bash; title: ; notranslate">curl -O http://effbot.org/media/downloads/elementtree-1.2.6-20050316.tar.gz
tar xzf elementtree-1.2.6-20050316.tar.gz
cd elementtree-1.2.6-20050316
sudo python setup.py install</pre>
<p>Now that&#8217;s element tree sorted. Let&#8217;s get simple JSON also:</p>
<pre class="brush: bash; title: ; notranslate">cd ../
curl -O http://pypi.python.org/packages/source/s/simplejson/simplejson-2.3.2.tar.gz#md5=0863e016f682f06ead07dd9efad95194
tar xzf simplejson-2.3.2.tar.gz
cd simplejson-2.3.2
sudo python setup.py install</pre>
<p>You should be all sorted now. So let&#8217;s try the scrobbling script again:</p>
<pre class="brush: bash; title: ; notranslate">cd ~/Library/plextrakt
python script.py</pre>
<p>If all&#8217;s well, which I hope it is, you should see some output put to the screen:</p>
<pre class="brush: bash; title: ; notranslate">Started monitoring a MacOSX running 10.7.2 with PMS Version 0.9.5.2-bf472e4
This plugin is in version 0.1 and is monitoring the log at /Users/voidet/Library/Logs/Plex Media Server.log
additional data is collected from PMS running at http://localhost:32400/ and reported to trakt.tv with username voidet.</pre>
<p>That&#8217;s right, my username is voidet. If you don&#8217;t see something like this, you borked it, or your setup is not entirely&#8230;. set, up. Leave a comment and I&#8217;ll try help you figure it out.</p>
<p>So you have the scrobbler running, awesome! But what if you reboot, how do we get this going automatically again. Well let&#8217;s add this into a LaunchAgent and call it a day. In terminal do this:</p>
<pre class="brush: bash; title: ; notranslate">vi ~/Library/LaunchAgents/tv.trakt.plist</pre>
<p>Next up, press &#8220;i&#8221; and paste this in:</p>
<pre class="brush: bash; title: ; notranslate">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
	&lt;dict&gt;
		&lt;key&gt;KeepAlive&lt;/key&gt;
			&lt;true/&gt;
		&lt;key&gt;Label&lt;/key&gt;
			&lt;string&gt;tv.trakt.plist&lt;/string&gt;
		&lt;key&gt;ProgramArguments&lt;/key&gt;
			&lt;array&gt;
			&lt;string&gt;/usr/bin/python&lt;/string&gt;
			&lt;string&gt;/Users/USERNAME_HERE/Library/plextrakt/script.py&lt;/string&gt;
			&lt;/array&gt;
		&lt;key&gt;RunAtLoad&lt;/key&gt;
			&lt;true/&gt;
	&lt;/dict&gt;
&lt;/plist&gt;</pre>
<p>Ensure you change USERNAME_HERE to be the home directory name of your user. For me, it is voidet. To save press escape then shift + zz. Saved and ready to load, on load&#8230;</p>
<pre class="brush: bash; title: ; notranslate">launchctl load -w ~/Library/LaunchAgents/tv.trakt.plist</pre>
<p>Reboot and start watching some TV shows or Movies, because they should start coming into Trakt.tv website where you can start getting great recommendations. Big thanks to Tester22 for they&#8217;re amazing work, and merging my pull request, also to the hard work of Trakt team and Plex hackers to get Plex on my AppleTV2!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2012/plex-trakt-a-scrobbling-love-story/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Remember Me: A CakePHP Persistent Login Plugin</title>
		<link>http://www.jotlab.com/2011/remember-me-another-cakephp-persistent-login-plugin</link>
		<comments>http://www.jotlab.com/2011/remember-me-another-cakephp-persistent-login-plugin#comments</comments>
		<pubDate>Tue, 06 Sep 2011 10:36:11 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[auth]]></category>
		<category><![CDATA[cookies]]></category>
		<category><![CDATA[login]]></category>
		<category><![CDATA[me]]></category>
		<category><![CDATA[persistent]]></category>
		<category><![CDATA[remember]]></category>
		<category><![CDATA[remember me]]></category>
		<category><![CDATA[sessions]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4663</guid>
		<description><![CDATA[After creating numerous CMS&#8217;s and getting client questions asking how come they don&#8217;t get automatically logged into their administration panel after 2 months of inactivity I decided to make Yet Another Remember Me Plugin (YARMP) &#8211; Yeh, I made that acronym up, it happens. Why... <span><a href="http://www.jotlab.com/2011/remember-me-another-cakephp-persistent-login-plugin" title="Remember Me: A CakePHP Persistent Login Plugin" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>After creating numerous CMS&#8217;s and getting client questions asking how come they don&#8217;t get automatically logged into their administration panel after 2 months of inactivity I decided to make Yet Another Remember Me Plugin (YARMP) &#8211; Yeh, I made that acronym up, it happens.</p>
<h2>Why Remember Me</h2>
<p>Remember me isn&#8217;t just a standard set a cookie and run with it type persistent login handler. Instead remember me uses salted tokens alongside the user&#8217;s auto login cookie to protect against session hijacking. The basic gist of the cycle is that a user is given a cookie with a unique set of authentication tokens. When a new login is made these tokens change, and if the user who presents a cookie to renew a session does no longer comply with these tokens, they&#8217;re denied and all cookies and sessions are flushed out!</p>
<p>What is different from this plugin to that of the others I find on google? Well RememberMe is secure, as secure as the word can be when written on the internet. It uses a token for its authenticity which is salted to ensure no cookie hijacking is possible. It is also extremely easy to set up. Just clone the plugin, add it to your app controller, add in one line to your login method and add a checkbox to your form. You&#8217;ll then have tasty cookies remembering your authenticated users with extremely minimal risk of cookie hijacking.</p>
<h2>Installing Remember Me</h2>
<p>First watch <a href="http://www.youtube.com/watch?v=GGIeJswiJU4">this</a>. Now install Remember me as a plugin through GitHub:</p>
<p><strong>Github:</strong> <a href="https://github.com/voidet/remember_me">https://github.com/voidet/remember_me</a></p>
<pre class="brush: php; title: ; notranslate">cd myapp
git clone git://github.com/voidet/remember_me.git remember_me</pre>
<p>or if you&#8217;re using submodules which you should be:</p>
<pre class="brush: php; title: ; notranslate">cd myapp
git submodule add git://github.com/voidet/remember_me.git app/plugins/remember_me
git submodule init
git submodule update</pre>
<p>You will also need to add two fields in your database that you use for user auth. The default names are token and token_salt. You can override which is shown in the next section.</p>
<p>From there it is a matter of adding it into your controller and ensuring you have your form fields linked up to what RememberMe is listening for.</p>
<h2>Adding Remember Me to your application</h2>
<p>Firstly you will need to add RememberMe to your controller, probably best to do this in app_controller.php so it is fired on all pages that use it:</p>
<pre class="brush: php; title: ; notranslate">var $components = array('RememberMe.RememberMe');</pre>
<p>After that you will need to tell RememberMe what form field you are using to activate the remember me function (like the name of a checkbox on a login screen that asks you if you want to be remembered for a while). The default is &#8220;remember_me&#8221; but if you want to change it you could do something like:</p>
<pre class="brush: php; title: ; notranslate">var $components = array('RememberMe.RememberMe' =&gt; array('field_name' =&gt; 'i_am_so_custom_it_hurts');</pre>
<p>Now add this field into your form and once posted you can set up the RememberMe cookie! Like:</p>
<pre class="brush: php; title: ; notranslate">function members_login() {
    if ($this-&gt;Auth-&gt;user()) {
        if (!empty($this-&gt;data)) {
            $this-&gt;RememberMe-&gt;setRememberMe($this-&gt;data[$this-&gt;Member-&gt;alias]);
        }
        $this-&gt;redirect($this-&gt;Auth-&gt;loginRedirect);
    }
}</pre>
<p>Of course this is an example. If you&#8217;re using auth remember to disable autoRedirect otherwise this action won&#8217;t be fired.</p>
<h2>Refresh my Cookie!</h2>
<p>A built in method comes with RememberMe. It is used to refresh the contents of the AuthCookie on page load, so it has the longest lifetime it can (without going outside the bounds of inactivity (which you set as a cookie setting)). To do so simply call:</p>
<pre class="brush: php; title: ; notranslate">if ($this-&gt;params['action'] != 'members_logout') {
        $this-&gt;RememberMe-&gt;checkUser();
    }</pre>
<p>This will refresh the cookie and log the user in if there is no Session available. Of course you don&#8217;t want to run this on logout methods, as you won&#8217;t be logged out!</p>
<h2>Feedback</h2>
<p>If you feel this plugin is missing something I would love to hear what you would like me to add in! But otherwise enjoy and please leave some feedback <img src='http://www.jotlab.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2011/remember-me-another-cakephp-persistent-login-plugin/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Appcelerator Titanium: Blurry Fonts &amp; iOS</title>
		<link>http://www.jotlab.com/2011/appcelerator-titanium-blurry-fonts-ios</link>
		<comments>http://www.jotlab.com/2011/appcelerator-titanium-blurry-fonts-ios#comments</comments>
		<pubDate>Wed, 03 Aug 2011 02:47:50 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[iphone]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[appcelerator]]></category>
		<category><![CDATA[blurry]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[fix]]></category>
		<category><![CDATA[help]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[q&a]]></category>
		<category><![CDATA[text]]></category>
		<category><![CDATA[titanium]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4698</guid>
		<description><![CDATA[So recently I&#8217;ve been working with some iPhone apps for work. After picking up a little bit of objective-c then dropping it for a few months I quickly found myself relearning from the ground up. After reviewing a fair few solutions outside of programming directly... <span><a href="http://www.jotlab.com/2011/appcelerator-titanium-blurry-fonts-ios" title="Appcelerator Titanium: Blurry Fonts &#038; iOS" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>So recently I&#8217;ve been working with some iPhone apps for work. After picking up a little bit of objective-c then dropping it for a few months I quickly found myself relearning from the ground up. After reviewing a fair few solutions outside of programming directly in objective-c  I came across Appcelerator Titanium.</p>
<p>Titanium is a tool that converts Javascript code to a native iPhone, iPad &amp; Android application. When I say native I don&#8217;t mean it is simply a web form that uses the WebUI kit in iOS that some other convert tools use; nope native. I introduce Titanium now because it looks like this is a tool I will be using quite frequently in the near future.</p>
<p>Anyhow getting stuck into development I started with using TitaniumUI&#8217;s TableView. It was too easy except after each label I added to a TableViewRow the text was looking not so sharp. At first I thought it was my eyes. Turns out that labels in a TableViewRow with an automatic height make the font rendering a bit blurry.</p>
<p>After looking into the issue for a few more hours and no help on the Q&amp;A&#8217;s I decided to get going on a fix. What I came up with was basically generating a label, getting the height of the label, then using that height + 1 pixel to add to the TableViewRow&#8217;s height. An example of the fix result:</p>
<pre class="brush: jscript; title: ; notranslate">var win = Titanium.UI.currentWindow;

var yummydata = ['list', 'of', 'elements'];
var rowData = [];
for (var i = 0; i &lt; yummydata.length; i++) {
	var labelToPlace = Titanium.UI.createLabel({
		text: yummydata[i],
		textAlign: 'left'
	});
	var rowHeight = labelToPlace.height + 1;
	rowData[] = Titanium.UI.createTableViewRow({height: rowHeight});
}

var tableView = Titanium.UI.createTableView( { data : rowData } );
win.add(tableView);</pre>
<p>I&#8217;m not entirely sure why this occurs, and I am fairly new to titanium, but am wondering if it has anything to do with the Row adding in 1px additional for the row separator. Either way this easy fix got the text crisp again.</p>
<p>From here no more introductions <img src='http://www.jotlab.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2011/appcelerator-titanium-blurry-fonts-ios/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Form Keeper: Field Name Obfuscation for CakePHP Forms</title>
		<link>http://www.jotlab.com/2011/form-keeper-field-name-obfuscation-for-cakephp-forms</link>
		<comments>http://www.jotlab.com/2011/form-keeper-field-name-obfuscation-for-cakephp-forms#comments</comments>
		<pubDate>Sun, 27 Feb 2011 10:33:57 +0000</pubDate>
		<dc:creator>voidet</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[component]]></category>
		<category><![CDATA[components]]></category>
		<category><![CDATA[form]]></category>
		<category><![CDATA[formhelper]]></category>
		<category><![CDATA[formkeeper]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[helper]]></category>
		<category><![CDATA[keeper]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4650</guid>
		<description><![CDATA[I don&#8217;t like giving my end users information about the inner workings of the system they&#8217;re using. Even though the system itself might be secure in terms of what data it can take in, how it treats the data and what it returns, I still... <span><a href="http://www.jotlab.com/2011/form-keeper-field-name-obfuscation-for-cakephp-forms" title="Form Keeper: Field Name Obfuscation for CakePHP Forms" rel="bookmark">[+]</a></span>]]></description>
				<content:encoded><![CDATA[<p>I don&#8217;t like giving my end users information about the inner workings of the system they&#8217;re using. Even though the system itself might be secure in terms of what data it can take in, how it treats the data and what it returns, I still don&#8217;t like the fact extra, inner system specs are publicly visible to the end user.</p>
<h2>What Is Form Keeper?</h2>
<p>This is where Form Keeper steps in. Form Keeper is a CakePHP 1.3 plugin that takes over from where CakePHP&#8217;s awesome Form Helper left off. Usually your forms would be constructed in a way like:</p>
<pre class="brush: php; title: ; notranslate">&lt;input name=&quot;data[ModelName][field_name]&quot; id=&quot;ModelNameFieldName&quot; value=&quot;&quot; type=&quot;hidden&quot; /&gt;</pre>
<p>This is all well and good. PHP will take those posted results, turn the data into a multidimensional array, pass it onto CakePHP and CakePHP will handle the rest, like a boss. The only problem with this is, it leaves a bad feeling in my stomach, in that, the end user knows your models, your field names in the database. Now there may be no immediate security threat with that, but less information provided, means less information for the end user to get construct blueprints of your database. So with FormKeeper you get:</p>
<p>
<pre class="brush: xml; title: ; notranslate">&lt;input name=&quot;6a03949c66c7cc5c7bce550c21308659c1d848a7&quot; type=&quot;text&quot; maxlength=&quot;40&quot; id=&quot;6a03949c66c7cc5c7bce550c21308659c1d848a7&quot;&gt;</pre>
</p>
<h2>What FormKeeper Does</h2>
<p>Form Keeper takes your forms, with all the power of CakePHP&#8217;s FormHelper, and adds a layer of one way encryption to your field names and field id&#8217;s. These values are firstly SHA1&#8242;d, cached locally into a hash table of your choice (using CakePHP&#8217;s cache engine, also check out my CakePHP SuperStack plugin for some cache redundancy action), then outputted to the view. When the form is then submitted, FormKeeper checks the value is in the hash table, reconstructs the data multidimensional array, and inserts the values.</p>
<p>Now if you are thinking &#8220;this is all good, but CakePHP&#8217;s security component is a mean bastard when it comes to forms and blackhole-ing&#8221;, well that part is covered. The tokenizing works just fine with this plugin, so you can use the security component alongside the FormKeeper for obfuscation and form tampering fun.</p>
<h2>Setting FormKeeper Up</h2>
<p>This is all well and good, but how do I set FormKeeper up? Quite easily, FormKeeper is a CakePHP plugin, so all you need to do is clone the Github repository to your plugins directory and then include both the plugin&#8217;s helper and component elements to either your AppController (if you want to use it site wide) or to the controller of your choice.</p>
<pre class="brush: bash; title: ; notranslate">git clone https://github.com/voidet/form_keeper app/plugins/form_keeper</pre>
<p>Or if you use submodules (like you should):</p>
<pre class="brush: bash; title: ; notranslate">git submodule add https://github.com/voidet/form_keeper app/plugins/form_keeper
git submodule init
git submodule update</pre>
<p>From there all that is left is adding it to your controller(s):</p>
<pre class="brush: php; title: ; notranslate">&lt;?php

class UsersController extends AppController {

	public $components = array('FormKeeper.FormKeeper');
	public $helpers = array('FormKeeper.FormKeeper');</pre>
<p>Now user views have access to the FormKeeper power! But how do views use it? Easy, instead of <strong>$this->Form</strong>, you will now use <strong>$this->FormKeeper</strong>, for everything!</p>
<pre class="brush: php; title: ; notranslate">&lt;?php

echo $this-&gt;FormKeeper-&gt;create();
echo $this-&gt;FormKeeper-&gt;input('username');
echo $this-&gt;FormKeeper-&gt;input('password');
echo $this-&gt;FormKeeper-&gt;input('remember_me', array('type' =&gt; 'checkbox'));
echo $this-&gt;FormKeeper-&gt;end('Login');</pre>
<h2>Configuration</h2>
<p>There are two parameters (so far) that you can change for the plugin. It&#8217;s as simple as adding in a configuration file into your app/config folder, named form_keeper.php, app/config/form_keeper.php. The two parameters that can be changed is the salt string to encrypt with (if not provided the Security.salt value in core.php will be used) and the cache config that you want to use (by default it will be the default core cache config, which is usually file). So create app/config/form_keeper.php and add in:</p>
<pre class="brush: php; title: ; notranslate">&lt;?php

$config['FormKeeper'] = array(
	'salt' =&gt; 'ixNE257AeJI2mbVicRwjEFM169seG59lzmxP8N4Z',
	'cacheKey' =&gt; 'default',
);</pre>
<p>Also please note that the Id&#8217;s of the input field are also hashed. To avoid this, you can simply provide an id in your input options, as you would to override cake&#8217;s default ID. This is primarily used for object selection in the DOM with jQuery etc.</p>
<h2>Security Component</h2>
<p>CakePHP has an excellent form tampering protection method that comes standard with the Security Component. It basically takes all the form fields in the view/layout between create and end, serializes their names and then hashes this into a token. The token is stored in a hidden field within the form, and once posted back is then checked against the posted data keys. If there is any variance in the rehashed field names that have been posted and in the token field then the user&#8217;s request will be blackHoled, effectively white screen of death (which can be easily overridden for your security pleasure).</p>
<p>FormKeeper maintains this level of security and functionality of CakePHP&#8217;s security component. The way FormKeeper achieves this is simply down to order of execution. FormKeeper taps into the power of the Form Helper by overriding how it creates field names. So basically Security Component generates it&#8217;s tokens (found in Form::secure()) against FormKeepers new field names. Coming back is also a matter of execution. FormKeeper remaps/restitches the hashed/protected field names back into the proper data[Model][fieldname] structure, because Security Component get&#8217;s its eager hands on the data ready to crush with the blackHole method. So in effect FormKeeper gets in and out before Security component knows any different.</p>
<h2>Thats It!</h2>
<p>This is an early release of the plugin, and is not tested in production just yet. Expect many updates to come in the near future, and hopefully some test cases to be written. Also if you&#8217;re using the security component, then please include it after the FormKeeper component at this point in time, as that is what has been *thoroughly* tested with. Thank you in advance for any comments and feedback left! If you&#8217;re having issues with the plugin, I usually respond pretty fast with updates or advice. Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2011/form-keeper-field-name-obfuscation-for-cakephp-forms/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.272 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2013-05-25 13:00:05 -->

<!-- Compression = gzip -->