<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>Ruby, Rails, and Randomness - Blog</title>
  <id>tag:www.naffis.com,2008:mephisto/</id>
  <generator uri="http://mephistoblog.com" version="0.7.3">Mephisto Noh-Varr</generator>
  <link href="http://www.naffis.com/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://www.naffis.com/" rel="alternate" type="text/html"/>
  <updated>2008-07-24T16:47:55Z</updated>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2008-07-24:2607</id>
    <published>2008-07-24T16:40:00Z</published>
    <updated>2008-07-24T16:47:55Z</updated>
    <link href="http://www.naffis.com/2008/7/24/freezing-edge-rails-in-edge" rel="alternate" type="text/html"/>
    <title>Freezing Edge Rails in Edge</title>
<content type="html">
            &lt;p&gt;If you've frozen rails to the latest version or have the latest gem installed and you're trying to freeze to anything other than 'edge' using the TAG= syntax, it no longer works. &lt;/p&gt;

&lt;p&gt;&lt;pre&gt;
rake rails:freeze:edge TAG=rel_2-0-0_RC2
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Instead use:&lt;/p&gt;

&lt;p&gt;
&lt;pre&gt;
rake rails:freeze:edge RELEASE=2.0.0_RC2
&lt;/pre&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2008-06-30:2397</id>
    <published>2008-06-30T18:32:00Z</published>
    <updated>2008-06-30T18:32:39Z</updated>
    <link href="http://www.naffis.com/2008/6/30/announcing-the-crowdsound-july-2008-release" rel="alternate" type="text/html"/>
    <title>Announcing the CrowdSound July 2008 Release</title>
<content type="html">
            &lt;p&gt;Intridea is proud to announce the July 2008 release of CrowdSound, the social feedback tool that opens up new lines of communication between you and your customers. The social feedback tool allows enterprise websites and social networking sites to gather, organize and respond to suggestions from their customers—for just $10 per month. Because CrowdSound requires only minimal Web developer knowledge to implement, businesses of all sizes can quickly realize the power of interactive, Web-based customer feedback. &lt;/p&gt;

&lt;p&gt;The latest release of CrowdSound has many new much-requested features:&lt;/p&gt;

&lt;p&gt;
* Customize the widget's colors and look and feel&lt;br /&gt;
* A hosted website in addition to the widget&lt;br /&gt;
* Custom suggestion buckets&lt;br /&gt;
* Private suggestions for sensitive topics&lt;br /&gt;
* SSL encryption&lt;br /&gt;
* iPhone integration&lt;br /&gt;
* User-selectable, customizable categories for each suggestion&lt;br /&gt;
* Profanity filtering to focus on constructive criticism&lt;br /&gt;
* ...and many more!&lt;br /&gt;
&lt;/p&gt;

&lt;p&gt;Using CrowdSound, companies can open a direct line of communications with customers via the Web—transparently gathering their suggestions without forcing them to navigate away from their page. The standards-based, highly customizable tool seamlessly integrates with an enterprise website or social networking site while providing a tailored look-and-feel that reinforces the company brand.  &lt;/p&gt; 

&lt;p&gt;For additional details and to try it out, visit &lt;a href='http://www.crowdsound.com'&gt;CrowdSound.com&lt;/a&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2008-04-28:2280</id>
    <published>2008-04-28T15:20:00Z</published>
    <updated>2008-04-28T15:26:10Z</updated>
    <category term="intridea"/>
    <category term="press"/>
    <link href="http://www.naffis.com/2008/4/28/intridea-featured-in-washington-post" rel="alternate" type="text/html"/>
    <title>Intridea featured in Washington Post</title>
<content type="html">
            &lt;p&gt;The Washington Post recently profiled Intridea and our products on its WASHBIZ Blog and in print on Monday, April 28, 2008; Page D04. The article explains our MediaPlug, Scalr, SocialSpring, and Smarkr products, and goes on to say that &quot;Intridea may be the model company for the modern Internet economy.&quot;&lt;/p&gt;

&lt;p&gt;
&lt;a href='http://www.washingtonpost.com/wp-dyn/content/article/2008/04/27/AR2008042701993.html?referrer=emailarticle'&gt;Click here to read the entire article&lt;/a&gt;
&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2008-04-03:2229</id>
    <published>2008-04-03T19:17:00Z</published>
    <updated>2008-04-03T19:17:38Z</updated>
    <link href="http://www.naffis.com/2008/4/3/intridea-s-scalr-on-techcrunch-and-aws" rel="alternate" type="text/html"/>
    <title>Intridea's Scalr on TechCrunch and AWS</title>
<content type="html">
            &lt;p&gt;Since open sourcing Scalr late yesterday we've been mentioned on &lt;a href='http://www.techcrunch.com/2008/04/03/scalr-the-auto-scaling-open-source-amazon-ec2-effort/'&gt;TechCrunch&lt;/a&gt; and the &lt;a href='http://aws.typepad.com/aws/2008/04/scalr-.html'&gt;Amazon Web Services Blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're extremely happy with the response so far. If anyone is interested in contributing to the project please visit the &lt;a href='http://scalr.intridea.com'&gt;Scalr Project&lt;/a&gt; on Google Code. &lt;/p&gt;

&lt;p&gt;&lt;a href='http://www.techcrunch.com/2008/04/03/scalr-the-auto-scaling-open-source-amazon-ec2-effort/'&gt;&lt;img src='http://www.intridea.com/assets/2008/4/3/Picture_2.png'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href='http://aws.typepad.com/aws/2008/04/scalr-.html'&gt;&lt;img src='http://www.intridea.com/assets/2008/4/3/Picture_1.png'&gt;&lt;/a&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2008-04-02:2225</id>
    <published>2008-04-02T16:05:00Z</published>
    <updated>2008-04-02T09:31:49Z</updated>
    <category term="aws"/>
    <category term="ec2"/>
    <category term="hosting"/>
    <category term="scalr"/>
    <link href="http://www.naffis.com/2008/4/2/open-sourcing-scalr" rel="alternate" type="text/html"/>
    <title>Open-sourcing Scalr</title>
<content type="html">
            &lt;p&gt;&lt;a href='http://www.intridea.com'&gt;Intridea&lt;/a&gt; is officially open-sourcing Scalr - a redundant, self-curing, and self-scaling hosting environment build on top of Amazon's EC2.&lt;/p&gt; 

&lt;p&gt;&lt;img src='http://www.intridea.com/images/icon_scalr.png'&gt;&lt;/p&gt;

&lt;p&gt;Scalr utilizes EC2 to provide a multi-tiered hosting environment with pre-built images for load balancers, database servers, and application servers. Designed with flexibility in mind, users can further customize each type of machine to use as nodes in their server farm or customize a generic base image for any number of purposes. The application monitors and maintains the server farm by reconfiguring the entire cluster when machines fail or when new machines are inserted. Additionally Scalr can be setup to replace failed machines and scale up and down based on user configured thresholds.&lt;/p&gt;

&lt;p&gt;The system was initially designed for &lt;a href='http://mediaplug.intridea.com'&gt;MediaPlug&lt;/a&gt;, a white label audio, video, and image transcoding service that needed to scale based on customer demand.&lt;/p&gt;

&lt;p&gt;The project can be found at &lt;a href='http://scalr.intridea.com'&gt;http://scalr.intridea.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is still very young, but we're hoping that by open sourcing it the AWS development community can turn this into a robust hosting platform give users an alternative to the current fee based services available.&lt;/a&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2008-01-16:2112</id>
    <published>2008-01-16T16:35:00Z</published>
    <updated>2008-02-13T20:57:26Z</updated>
    <link href="http://www.naffis.com/2008/1/16/actsasconference" rel="alternate" type="text/html"/>
    <title>ActsAsConference</title>
<content type="html">
            &lt;p&gt;Josh Owens and I will be presenting at the upcoming &lt;a href='http://www.actsasconference.com/'&gt;acts_as_conference&lt;/a&gt; February 8th and 9th in Orlando Florida. &lt;/p&gt;

&lt;p&gt;&lt;a href='http://www.actsasconference.com/speakers/dave-naffis-josh-owens'&gt;Adding Media to Your Rails Application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding media such as audio and video to a web application can be costly and time consuming. In this talk we'll cover the fundamentals and break down several solutions for uploading large files and transcoding audio and video in Rails.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://www.naffis.com/assets/2008/2/13/aac_adding_media.zip'&gt;Presentation slides and files&lt;/a&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2007-10-05:1082</id>
    <published>2007-10-05T16:28:00Z</published>
    <updated>2008-04-15T23:13:02Z</updated>
    <link href="http://www.naffis.com/2007/10/5/campfire-svn-and-email-notification" rel="alternate" type="text/html"/>
    <title>Campfire SVN and email notification</title>
<content type="html">
            &lt;p&gt;
Here's a quick way to add Subversion notification for &lt;a href='http://www.campfirenow.com/'&gt;Campfire&lt;/a&gt; using &lt;a href='http://opensoul.org/2006/12/8/tinder-campfire-api'&gt;Tinder&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;Create svn-campfire.rb with the correct username and password:&lt;/p&gt;
&lt;pre&gt;&lt;code class='ruby'&gt;#!/usr/local/bin/ruby
require 'rubygems'
require 'tinder'

svnlook = &amp;quot;/usr/local/bin/svnlook&amp;quot;


campfire = Tinder::Campfire.new 'campfiresubdomain'
campfire.login 'user@example.com', 'password'
room = campfire.find_room_by_name('room name')
room.join

if ARGV.size &amp;gt; 1
  revision = ARGV[1]
  path = ARGV[0]
  # we're using this for multiple svn repos so parse the project name from the path
  project = ARGV[0].gsub(&amp;quot;/home/user/svn/&amp;quot;, '')
  
  author = `#{svnlook} author -r #{revision} #{path}`
  paths  = `#{svnlook} changed -r #{revision} #{path}`   
  log    = `#{svnlook} log -r #{revision} #{path}`
  message = [log,paths].join(&amp;quot;\n&amp;quot;).strip
  url = &amp;quot;Changeset \##{revision} by #{author} (http://trac.domain.com/trac/#{project}/changeset/#{revision})&amp;quot;

  room.speak(url)
  room.paste(message)
else
  room.speak(ARGV[0])
end

room.leave&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Add this to your SVN post-commit hook:&lt;/a&gt;
&lt;pre&gt;
/usr/local/bin/ruby /path/to/svn-campfire.rb &quot;$1&quot; &quot;$2&quot;
&lt;/pre&gt;

&lt;p&gt;Here's a quick way to send emails to campfire. Create an email address to use for sending messages to campfire and then create mailer-campfire.rb with your domain, usernames and passwords:&lt;/p&gt; 

&lt;pre&gt;&lt;code class='ruby'&gt;#!/usr/local/bin/ruby
require 'rubygems'
require 'action_mailer'
require 'tinder'
require 'net/pop'

# setup an email address to use for campfire
Net::POP3.delete_all(&amp;quot;domain.com&amp;quot;, nil, &amp;quot;user+domain.com&amp;quot;, &amp;quot;password&amp;quot;) do |m|
  campfire = Tinder::Campfire.new 'campfiresubdomain'
  campfire.login 'user@example.com', 'password'
  room = campfire.find_room_by_name('room name')
  room.join

  begin
    message = TMail::Mail.parse(m.pop)
    
    subject = message.subject if message.subject
    sender = message.from.first if message.from
    body = message.body if message.body
    
    room.speak(&amp;quot;I've received an email from #{sender} with the subject '#{subject}'&amp;quot;)
    room.paste(body)                
  rescue Exception =&amp;gt; e
  end        
    
  room.leave
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then create a cron job to fire this off every minute:&lt;/p&gt;
&lt;pre&gt;
* * * * * /usr/local/bin/ruby /home/path/to/mailer-campfire.rb
&lt;/pre&gt;

&lt;p&gt;Next step, create a Jabber gateway for Campfire using Tinder.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2007-08-10:736</id>
    <published>2007-08-10T19:48:00Z</published>
    <updated>2007-08-10T20:10:37Z</updated>
    <link href="http://www.naffis.com/2007/8/10/with_scope-is-protected" rel="alternate" type="text/html"/>
    <title>with_scope is protected</title>
<content type="html">
            &lt;p&gt;
I've seen a lot of people run into trouble using with_scope on edge. It's now a protected method &lt;a href='http://dev.rubyonrails.org/ticket/8524'&gt;Ticket: http://dev.rubyonrails.org/ticket/8524&lt;/a&gt;. 
&lt;/p&gt;

&lt;p&gt;
The solution? Don't use it or use it sparingly. They made it protected for a reason - to protect people from abusing it - but if you absolutely must have at it then you can do something like this: 
&lt;/p&gt;

&lt;p&gt;
&lt;pre&gt;&lt;code class='ruby'&gt;klass.send(:with_scope, :find =&amp;gt; { :conditions =&amp;gt; conditions }) { yield }&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
So say you want to scope your finders to retrieve only MyModel objects of type 'super':
&lt;/p&gt;

&lt;p&gt;
&lt;pre&gt;&lt;code class='ruby'&gt;def my_super_duper_scoping(klass)
  klass.send(:with_scope, :find =&amp;gt; { :conditions =&amp;gt; &amp;quot;type = 'super'&amp;quot; }) { yield }
end

my_super_models = my_super_duper_scoping(MyModel) do
    MyModel.find(:all)
  end

my_super_models_named_david = my_super_duper_scoping(MyModel) do
    MyModel.find(:all, :conditions =&amp;gt; [&amp;quot;name = '?'&amp;quot;, 'david')
  end&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
Using self.class.send is a way to get around public and private checking in ruby 1.8. Use with caution.
&amp;lt;/P
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2007-08-06:397</id>
    <published>2007-08-06T23:08:00Z</published>
    <updated>2008-06-03T13:15:25Z</updated>
    <category term="ajax"/>
    <category term="rails"/>
    <category term="tags"/>
    <category term="type-ahead"/>
    <link href="http://www.naffis.com/2007/8/6/tag-typeahead-with-multiple-items" rel="alternate" type="text/html"/>
    <title>Tag Type-Ahead with Multiple Items</title>
<content type="html">
            &lt;p&gt;
If you've ever implemented type-ahead functionality and wondered how to get it to work when users are entering multiple items, like a list of comma delimited tags, here it is.
&lt;/p&gt;

&lt;p&gt;
You can, of course, roll your own Javascript and Ajax calls but why bother when Rails gives us a nice Javascript helper to handle this. The helper is &lt;b&gt;text_field_with_auto_complete&lt;/b&gt; but this defaults to the current controller. For my purposes tags can be entered on different areas of the site for different models so I used text_field_with_auto_complete with one modification:
&lt;/p&gt;

&lt;pre&gt;&lt;code class='ruby'&gt;def text_field_with_auto_complete_with_custom_url(object, method, url_options = {}, tag_options = {}, completion_options = {})
  (completion_options[:skip_style] ? &amp;quot;&amp;quot; : auto_complete_stylesheet) +
  text_field(object, method, tag_options) +
  content_tag(&amp;quot;div&amp;quot;, &amp;quot;&amp;quot;, :id =&amp;gt; &amp;quot;#{object}_#{method}_auto_complete&amp;quot;, :class =&amp;gt; &amp;quot;auto_complete&amp;quot;) +
  auto_complete_field(&amp;quot;#{object}_#{method}&amp;quot;, { :url =&amp;gt; { :action =&amp;gt; &amp;quot;auto_complete_for_#{object}_#{method}&amp;quot; }.update(url_options) }.update(completion_options))
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Note: text_field_with_auto_complete is deprecated and will be moved to a plugin in Rails 2.0.
&lt;/p&gt;

&lt;p&gt;
The third parameter is now a hash of optional url arguments (action, controller, etc). You can also monkey patch the original method or better use some alias/alias_method_chain trickery in your own app. In your view call the newly created helper with your arguments. 
&lt;/p&gt;

&lt;pre&gt;&lt;code class='ruby'&gt;&amp;lt;label for=&amp;quot;tag_name&amp;quot;&amp;gt;Tags:&amp;lt;/label&amp;gt;
&amp;lt;%= text_field_with_auto_complete_with_custom_url :tag, :names, 
    {:controller =&amp;gt; &amp;quot;tagging_demo&amp;quot;}, {}, { :tokens =&amp;gt; ','} %&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Include the necessary javascript libraries (prototype and scriptaculous):
&lt;/p&gt;

&lt;pre&gt;&lt;code class='ruby'&gt;&amp;lt;%= javascript_include_tag 'prototype' %&amp;gt;
&amp;lt;%= javascript_include_tag 'scriptaculous' %&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
You can set the controller and action to whatever you like but it defaults to a method in the form of auto_complete_for_object_method. Going with that your action could look something like this:
&lt;/p&gt;

&lt;pre&gt;&lt;code class='ruby'&gt;def auto_complete_for_tag_names
  @tags = Tag.find(:all, 
    :conditions =&amp;gt; [&amp;quot;name like ?&amp;quot;, &amp;quot;%#{params[:tag][:names]}%&amp;quot;], 
    :order =&amp;gt; 'name DESC', :limit =&amp;gt; 20)
  render :layout =&amp;gt; false
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
And your view (auto_complete_for_tag_names.html.erb):
&lt;/p&gt;

&lt;pre&gt;&lt;code class='html'&gt;&amp;lt;ul class=&amp;quot;tags&amp;quot;&amp;gt;
&amp;lt;% for tag in @tags do -%&amp;gt;
  &amp;lt;li class=&amp;quot;tag&amp;quot;&amp;gt;&amp;lt;%= tag.name -%&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;% end -%&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
That's all there is to it. The magic comes from providing a token to Ajax.Autocompleter which we provided in the argument hash to text_field_with_auto_complete_with_custom_url ( { :tokens =&gt; ','} ).  You can use any delimiter you like. You can also customize the UI in your view and CSS.
&lt;/p&gt;

&lt;p&gt;
Here's a demo &lt;a href='http://www.naffis.com/demos/tagging_demo'&gt;http://www.naffis.com/demos/tagging_demo&lt;/a&gt;
&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2007-08-05:323</id>
    <published>2007-08-05T01:12:00Z</published>
    <updated>2008-04-01T02:35:58Z</updated>
    <link href="http://www.naffis.com/2007/8/5/racing-presidents" rel="alternate" type="text/html"/>
    <title>Racing Presidents</title>
<content type="html">
            &lt;p&gt;Nats v Reds&lt;/p&gt;
&lt;img src='http://farm2.static.flickr.com/1195/1011779767_79b730b3c8.jpg?v=0'&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2007-06-26:112</id>
    <published>2007-06-26T13:54:00Z</published>
    <updated>2007-06-26T14:13:27Z</updated>
    <link href="http://www.naffis.com/2007/6/26/upcoming-conferences" rel="alternate" type="text/html"/>
    <title>Upcoming Conferences</title>
<content type="html">
            &lt;p&gt;I'll be presenting at two upcoming conferences:&lt;/p&gt;

&lt;p&gt;
&lt;a href='http://www.agile2007.org/agile2007/index.php?page=sub/&amp;amp;id=855'&gt;Agile 2007&lt;/a&gt;, August 13-17, Washington, DC
&lt;/p&gt;

&lt;p&gt;
&lt;a href='http://conferences.oreillynet.com/cs/railseurope2007/view/e_sess/14238'&gt;RailsConf Europe 2007&lt;/a&gt;, September 17-19, Berlin, Germany
&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2007-05-22:95</id>
    <published>2007-05-22T23:06:00Z</published>
    <updated>2007-05-22T23:14:27Z</updated>
    <category term="acts_as_viewable"/>
    <category term="plugins"/>
    <category term="projects"/>
    <category term="rails"/>
    <category term="ruby"/>
    <link href="http://www.naffis.com/2007/5/22/tracking-views-in-rails" rel="alternate" type="text/html"/>
    <title>Tracking Views in Rails</title>
<content type="html">
            &lt;p&gt;ActsAsViewable is plugin that allows you to track page and asset views in your Rails application. For example, you can use it to track how many times a page is visited or how many times a particular image is viewed.&lt;/p&gt;


	&lt;p&gt;Trac: &lt;a href='http://trac.intridea.com/trac/public/wiki/ActsAsViewable'&gt;http://trac.intridea.com/trac/public/wiki/ActsAsViewable&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Subversion repository: &lt;a href='http://svn.intridea.com/svn/public/acts_as_viewable/'&gt;http://svn.intridea.com/svn/public/acts_as_viewable&lt;/a&gt;&lt;/p&gt;


Installation:
&lt;pre&gt;&lt;code class='ruby'&gt;script/plugin install http://svn.intridea.com/svn/public/acts_as_viewable&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;OR&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;cd vendor/plugins
svn co http://svn.intridea.com/svn/public/acts_as_viewable&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Create the tables where views will be tracked:&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;class CreateViewings &amp;lt; ActiveRecord::Migration
  def self.up
    create_table :viewings do |t|
      t.column :viewable_type,  :string
      t.column :viewable_id,    :integer
      t.column :views,          :integer,   :default =&amp;gt; 0
      t.column :created_at,     :datetime, :null =&amp;gt; false
      t.column :updated_at,     :datetime
    end
  end

  def self.down
    drop_table :viewings
  end
end&lt;/code&gt;&lt;/pre&gt;

Set the objects you want to track views for:
&lt;pre&gt;&lt;code class='ruby'&gt;class SomeAsset &amp;lt; ActiveRecord::Base
  acts_as_viewable
end&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Now you can increment views for these objects wherever you need to. For example in the show action of our SomeAssetController:&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;class SomeAssetController &amp;lt; ApplicationController
  def show
    @some_asset = SomeAsset.find(params[:id])
    @some_asset.increment_views
  end
end&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;To get the number of views:&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;@some_asset.views&lt;/code&gt;&lt;/pre&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2007-05-22:94</id>
    <published>2007-05-22T22:59:00Z</published>
    <updated>2007-05-22T23:10:21Z</updated>
    <category term="plugins"/>
    <category term="projects"/>
    <category term="rails"/>
    <category term="ruby"/>
    <category term="sessionexpiration"/>
    <link href="http://www.naffis.com/2007/5/22/automatically-expiring-sessions-in-rails" rel="alternate" type="text/html"/>
    <title>Automatically Expiring Sessions in Rails</title>
<content type="html">
            &lt;p&gt;SessionExpiration is plugin that allows you to expire sessions after X seconds of 
inactivity. Useful for when you want to automatically log out users if they&#8217;re idle.&lt;/p&gt;


	&lt;p&gt;Trac: &lt;a href='http://trac.intridea.com/trac/public'&gt;http://trac.intridea.com/trac/public&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Subversion repository: &lt;a href='http://svn.intridea.com/svn/public/session_expiration/'&gt;http://svn.intridea.com/svn/public/session_expiration/&lt;/a&gt;&lt;/p&gt;


Installation:
&lt;pre&gt;&lt;code class='ruby'&gt;script/plugin install http://svn.intridea.com/svn/public/session_expiration&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;OR&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;cd vendor/plugins
svn co http://svn.intridea.com/svn/public/session_expiration&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Specify when to expire session in your ApplicationController to do it site wide or you can do it for specific controllers:&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;class ApplicationController
  expire_session_in 5.minutes
end&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;If you want to run a method when the session expires use this:&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;class ApplicationController
  expire_session_in 5.minutes, :after_expiration =&amp;gt; :some_method

  def some_method
    flash[:notice] = &amp;quot;You have been logged out due to inactivity&amp;quot;
  end
end&lt;/code&gt;&lt;/pre&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>naffis</name>
    </author>
    <id>tag:www.naffis.com,2007-05-20:93</id>
    <published>2007-05-20T22:41:00Z</published>
    <updated>2007-05-22T23:20:44Z</updated>
    <link href="http://www.naffis.com/2007/5/20/blogging-again-finally" rel="alternate" type="text/html"/>
    <title>Blogging Again, Finally</title>
<content type="html">
            &lt;p&gt;I think my blog has been down for at least 4 months now. I&#8217;ve had very little time to spare between work and the rest of life. I&#8217;m glad to say it&#8217;s finally back up and ported from Typo to Mephisto sitting on a brand new server. All the old blog posts and comments should still be there and the old &lt;span class='caps'&gt;URL&lt;/span&gt;&#8217;s should map to the same posts.&lt;/p&gt;


	&lt;p&gt;Also, this blog will probably take on a more personal tone now since most of the technical stuff will also be posted on the Intridea blog &#8211; &lt;a href='http://www.intridea.com/blog'&gt;http://www.intridea.com/blog&lt;/a&gt;. If you just want the Rails stuff sans me whining about life then check that out instead.&lt;/p&gt;


	&lt;p&gt;With any luck I&#8217;ll be making a few posts soon.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://www.naffis.com/">
    <author>
      <name>dnaffis</name>
    </author>
    <id>tag:www.naffis.com,2006-12-11:33</id>
    <published>2006-12-11T13:38:00Z</published>
    <updated>2007-05-22T22:10:17Z</updated>
    <category term="ajax"/>
    <category term="drag and drop"/>
    <category term="image"/>
    <category term="rails"/>
    <category term="rmagick"/>
    <category term="ruby"/>
    <category term="sorting"/>
    <category term="upload"/>
    <link href="http://www.naffis.com/2006/12/11/ajax-uploads-image-manipulation-drag-and-drop-sorting" rel="alternate" type="text/html"/>
    <title>Ajax uploads? Image manipulation &amp; drag-and-drop sorting.</title>
<content type="html">
            &lt;p&gt;Wouldn&#8217;t it be nice to allow uploads in a cool Ajaxy way? Well, because of security restrictions it&#8217;s just not possible. There are however ways to create the same effect.&lt;/p&gt;


	&lt;p&gt;Here&#8217;s a quick demo of an ajax-ish image upload as well as some image manipulation functionality, and drag and drop sorting. I&#8217;m not sure this will work on all browsers but it&#8217;s been tested successfully with most. This was created about 4 months ago and I never had time to polish any of it up so take what you can from it.&lt;/p&gt;


	&lt;p&gt;&lt;a href='http://www.naffis.com/demos/image_demo'&gt;http://www.naffis.com/demos/image_demo&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;First our layout (layouts/image_demo.rhtml):&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;&lt;code class='html'&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Strict//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/strict.dtd&amp;quot;&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;Image Demo&amp;lt;/title&amp;gt;
        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot;&amp;gt;        
        &amp;lt;%= javascript_include_tag :defaults %&amp;gt;
        &amp;lt;%= stylesheet_link_tag 'image_demo' %&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;div id=&amp;quot;titlebar&amp;quot;&amp;gt;Image Demo&amp;lt;/div&amp;gt;

        &amp;lt;%= render :partial =&amp;gt; &amp;quot;upload_form&amp;quot; %&amp;gt;    

        &amp;lt;div id=&amp;quot;centercontent&amp;quot;&amp;gt;
            &amp;lt;%= yield %&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div id=&amp;quot;next&amp;quot;&amp;gt;
            &amp;lt;%= link_to &amp;quot;Create Animated Gif&amp;quot;, :action =&amp;gt; &amp;quot;animate&amp;quot; %&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div id=&amp;quot;bottom&amp;quot;&amp;gt;
            &amp;amp;copy; naffis.com 2006 
        &amp;lt;/div&amp;gt;

    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;We&#8217;re going to extend the form_remote_tag to handle file uploads.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Drop this in your lib directory (lib/remote_uploads.rb):&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;module ActionView
  module Helpers
    module PrototypeHelper
      alias_method :form_remote_tag_old, :form_remote_tag
      def form_remote_tag(options = {})
        if options[:html] &amp;amp;&amp;amp; options[:html][:multipart]      
          uid = &amp;quot;a#{Time.now.to_f.hash}&amp;quot;                                
          &amp;lt;&amp;lt;-STR    
          &amp;lt;iframe name=&amp;quot;#{uid}&amp;quot; id=&amp;quot;#{uid}&amp;quot; src=&amp;quot;about:blank&amp;quot; style=&amp;quot;position:absolute;left:-100px;width:0px;height:0px;border:0px&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;
          &amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;#{url_for options[:url].update({:iframe_remote =&amp;gt; true})}&amp;quot; enctype=&amp;quot;multipart/form-data&amp;quot; target=&amp;quot;#{uid}&amp;quot; #{%(onsubmit=&amp;quot;#{options[:loading]}&amp;quot;) if options[:loading]}&amp;gt;
          STR
        else
          form_remote_tag_old(options)
        end
      end                             
    end
  end
end&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;&lt;strong&gt;Add the require in your environment.rb:&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;&lt;code class='ruby'&gt;require 'remote_uploads.rb'&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;This will create a custom form for file uploads (multipart =&amp;gt; true) that submits to a hidden iframe. If it&#8217;s not a file upload then it will revert to the standard form_remote_tag of PrototypeHelper.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Some boring half baked styles for our demo:&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;&lt;code class='css'&gt;body {
  background-color:        #FFFFFF;
  background-image:        url(/maps/images/gradient.jpg);
  background-repeat:    no-repeat;
  color:                            #666666;
  font-family:                arial, sans;
  font-size:                    100%;
  line-height:                1.7em;
  margin:                            1em 2em;
}

#titlebar {
  font-size:                     1.2em;
  border-bottom:                    2px solid #333333;
  margin-bottom:                    1em;
  padding-bottom:                1em;
}

h2 {
  font-size:                     1.2em;
}

ul.navigation {
  background-color:        #333333;
  padding:                        0em 0.5em;
  list-style-type:        none;
}

ul.navigation li {
  border-right:                1px solid #666666;
  display:                        inline;
}

.navigation a {
  color:                            #FFFFFF;
  padding:                        0.5em;
}

.description {
  font-size:                    1.2em;
}

.upload {
  font-size:                    1.2em;
}

strong {
  background-color:        #FFFF99;
}

#centercontent {
  width: 100%;
  text-align: center;
  margin-bottom:                1em;
  padding-bottom:                1em;                
  margin-top:                    1em;
  padding-top:                1em;
}

#bottom {                
    width: 100%;
    float: left;
    text-align: center;
    border-top:                    2px solid #333333;
    margin-top:                    1em;
    padding-top:                1em;
}

div.float {
  width: 120px;
  padding: 10px;
  float: left;
}

div.spacer {
  clear: both;
}

div.float img {
  margin-left: 5px;
  }

div.float p {
  font-size: 9px;
  text-align: center;
  }            

#image-list ul {    
  list-style: none;
}

#image-list ul li {
  list-style: none;
  display: inline; 
    float: left;  
    width: 120px; 
    height: 120px; 
    padding: 10px;
  border: 1px solid #000;      
}&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;We&#8217;re using Sean Treadway&#8217;s responds_to_parent plugin (http://sean.treadway.info/svn/plugins/responds_to_parent/) to execute our &lt;span class='caps'&gt;RJS&lt;/span&gt; generated javascript in the parent window instead of the iframe which the file upload is submitted to. There are other ways of doing this that use less code but the plugin is simple so why not use it?&lt;/p&gt;


	&lt;p&gt;Everything from this point on is pretty self explanitory. I can expand on it later but here&#8217;s the rest of the code.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Our index:&lt;/strong&gt;
&lt;pre&gt;&lt;code class='html'&gt;&amp;lt;div id=&amp;quot;image-list&amp;quot;&amp;gt;
    &amp;lt;ul id=&amp;quot;sortable_list&amp;quot;&amp;gt;
        &amp;lt;% for @asset in @assets %&amp;gt;
            &amp;lt;%= render :partial =&amp;gt; &amp;quot;image_container&amp;quot;, :locals =&amp;gt; { :asset =&amp;gt; @asset } %&amp;gt;
        &amp;lt;% end %&amp;gt;
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;%= sortable_element('sortable_list', :constraint =&amp;gt; false, :url =&amp;gt; {:action =&amp;gt; :update_positions}) %&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


	&lt;p&gt;Some partials used above:&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;_image_container.rhtml&lt;/strong&gt;
&lt;pre&gt;&lt;code class='html'&gt;&amp;lt;li id=&amp;quot;item_&amp;lt;%= @asset.id %&amp;gt;&amp;quot; class=&amp;quot;float&amp;quot;&amp;gt;
    &amp;lt;%= render :partial =&amp;gt; &amp;quot;image_thumb&amp;quot;, :locals =&amp;gt; { :asset =&amp;gt; @asset } %&amp;gt;
&amp;lt;/li&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;_image_thumb.rhtml&lt;/strong&gt;
&lt;pre&gt;&lt;code class='html'&gt;&amp;lt;%= image_tag @asset.thumbnail, :border =&amp;gt; 2 %&amp;gt;
    &amp;lt;br&amp;gt;
    &amp;lt;%= link_to_remote(image_tag(&amp;quot;arrow_rotate_anticlockwise.png&amp;quot;, :border =&amp;gt; 0), :url =&amp;gt; {:action =&amp;gt; &amp;quot;rotate&amp;quot;, :id =&amp;gt; @asset.id, :direction =&amp;gt; &amp;quot;left&amp;quot;} ) %&amp;gt;
    &amp;amp;nbsp;
    &amp;lt;%= link_to_remote(image_tag(&amp;quot;cross.png&amp;quot;, :border =&amp;gt; 0), :url =&amp;gt; {:action =&amp;gt; &amp;quot;remove&amp;quot;, :id =&amp;gt; @asset.id} ) %&amp;gt;
    &amp;amp;nbsp;
    &amp;lt;%= link_to_remote(image_tag(&amp;quot;arrow_rotate_clockwise.png&amp;quot;, :border =&amp;gt; 0), :url =&amp;gt; {:action =&amp;gt; &amp;quot;rotate&amp;quot;, :id =&amp;gt; @asset.id, :direction =&amp;gt; &amp;quot;right&amp;quot;} ) %&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;_upload_form.rhtml&lt;/strong&gt;
&lt;pre&gt;&lt;code class='html'&gt;&amp;lt;%= form_remote_tag(:url =&amp;gt; { 
        :controller =&amp;gt; &amp;quot;image_demo&amp;quot;, 
        :action =&amp;gt; &amp;quot;create&amp;quot; },             
        :html =&amp;gt; {:multipart =&amp;gt; true}) %&amp;gt;
    &amp;lt;b&amp;gt;Picture:&amp;lt;/b&amp;gt;&amp;amp;nbsp;
    &amp;lt;%= file_field_tag &amp;quot;asset&amp;quot; %&amp;gt;&amp;amp;nbsp;
    &amp;lt;%= submit_tag &amp;quot;Upload&amp;quot; %&amp;gt;&amp;amp;nbsp;
&amp;lt;%= end_form_tag %&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


	&lt;p&gt;Our &lt;span class='caps'&gt;RJS&lt;/span&gt; to handle the create, remove, and rotate.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;create.rjs&lt;/strong&gt;
&lt;pre&gt;&lt;code class='ruby'&gt;if @asset.new_record?
  page.alert &amp;quot;There was a problem uploading your file:\n&amp;quot; +
  @asset.errors.full_messages.join(&amp;quot;\n&amp;quot;)
else
  page.insert_html :top, 'sortable_list', :partial =&amp;gt; 'image_container', :locals =&amp;gt; { :asset =&amp;gt; @asset } 
  page.visual_effect :highlight, &amp;quot;item_#{@asset.id}&amp;quot;
  page.sortable &amp;quot;sortable_list&amp;quot;, :constraint =&amp;gt; false, :url =&amp;gt; { :action =&amp;gt; :update_positions }
end&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;remove.rjs&lt;/strong&gt;
&lt;pre&gt;&lt;code class='ruby'&gt;page.remove &amp;quot;item_#{@asset_id}&amp;quot;
page.sortable &amp;quot;sortable_list&amp;quot;, :constraint =&amp;gt; false, :url =&amp;gt; { :action =&amp;gt; :update_positions }&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;rotate.rjs&lt;/strong&gt;
&lt;pre&gt;&lt;code class='ruby'&gt;page.replace_html &amp;quot;item_#{@asset.id}&amp;quot;, :partial =&amp;gt; 'image_thumb', :locals =&amp;gt; { :asset =&amp;gt; @asset } 
page.visual_effect :highlight, &amp;quot;item_#{@asset.id}&amp;quot;
page.sortable &amp;quot;sortable_list&amp;quot;, :constraint =&amp;gt; false, :url =&amp;gt; { :action =&amp;gt; :update_positions }&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Our controller:&lt;/strong&gt;
&lt;pre&gt;&lt;code class='ruby'&gt;class ImageDemoController &amp;lt; ApplicationController 
  layout 'image_demo'

  def index
    session[:uid] = Time.now.to_i unless session[:uid]      
    @assets = Asset.find(:all, 
                         :conditions =&amp;gt; [&amp;quot;user_id = ?&amp;quot;, session[:uid].to_i],
    :order =&amp;gt; &amp;quot;position&amp;quot;)    
  end

  def create
    @asset = Asset.new()
    @asset.uploaded_file = params['asset']
    @asset.position = 0
    @asset.user_id = session[:uid].to_i
    @asset.save    
    responds_to_parent do
      render :action =&amp;gt; 'create.rjs'
    end
    return
  end    

  def list
    @assets = Asset.find(:all, 
                         :conditions =&amp;gt; [&amp;quot;user_id = ?&amp;quot;, session[:uid].to_i],
    :order =&amp;gt; &amp;quot;position&amp;quot;)
  end  

  def update_positions
    params[:sortable_list].each_with_index do |id, position|
      Asset.update(id, :position =&amp;gt; position)
    end
    render :nothing =&amp;gt; true
  end

  def rotate
    @asset = Asset.find(params[:id])
    degrees = params[:direction] == &amp;quot;left&amp;quot; ? -90 : 90
    @asset.rotate(degrees)
  end

  def remove
    @asset_id = params[:id]
    Asset.delete(@asset_id)
  end

end&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Our asset model:&lt;/strong&gt;
&lt;pre&gt;&lt;code class='ruby'&gt;require 'RMagick'

class Asset &amp;lt; ActiveRecord::Base 

  def uploaded_file=(incoming_file)
    content_type = incoming_file.content_type.chomp
    if content_type.rindex(/image\/[(jpe?g)||(gif)]/)
      self.name = base_part_of(incoming_file.original_filename)

      base_dir = &amp;quot;/some/path/you/like&amp;quot;           

      # save original file
      self.original = &amp;quot;image_demo_assets/o_#{Time.now.utc.to_i}#{rand(1000000)}.&amp;quot;+self.name
      File.open(base_dir+self.original,File::CREAT|File::TRUNC|File::WRONLY,0666){ |f|
        f.write(incoming_file.read)
      }      

      self.resized = &amp;quot;image_demo_assets/r_#{Time.now.utc.to_i}#{rand(1000000)}.&amp;quot;+self.name
      resized = Magick::Image.read(base_dir+self.original).first
      resized.change_geometry!('500x500') { |cols, rows, img|
        img.resize!(cols, rows)
      }
      resized.write(base_dir+self.resized) 

      self.thumbnail = &amp;quot;image_demo_assets/t_#{Time.now.utc.to_i}#{rand(1000000)}.&amp;quot;+self.name        
      thumb = Magick::Image.read(base_dir+self.original).first
      thumb.change_geometry!('100x100') { |cols, rows, img|
        img.resize!(cols, rows)
      }
      thumb.write(base_dir+self.thumbnail)     

      self.save    
    end
  end

  def rotate(degrees) 
    base_dir = &amp;quot;/some/path/you/like&amp;quot;       
    #main photo
    image = Magick::ImageList.new(base_dir+self.original)
    image = image.rotate(degrees)
    image.write(base_dir+self.original)

    # resized
    resized = Magick::ImageList.new(base_dir+self.resized)
    resized = resized.rotate(degrees)
    resized.write(base_dir+self.resized)    

    # thumb
    thumb = Magick::ImageList.new(base_dir+self.thumbnail)
    thumb = thumb.rotate(degrees)
    thumb.write(base_dir+self.thumbnail)    
  end

  private

  def base_part_of(file_name)
    name = File.basename(file_name)
    name.gsub(/[^W._-]/, '')
    sanitize_filename(name)
  end

  # Fixes a 'feature' of IE where it passes the entire path instead of just the filename
  def sanitize_filename(value)
    #get only the filename (not the whole path)
    just_filename = value.gsub(/^.*(\\|\/)/, '')
    just_filename.gsub(/[^\w\.\-]/,'_') 
  end

end&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


Some suggestions:
&lt;ul&gt;
&lt;li&gt;Use form_for and get rid of some ugliness in the controller by using Asset.new(params[:asset]) instead of setting each value individually.&lt;/li&gt;
&lt;li&gt;Use simply_helpful for generiting your &lt;span class='caps'&gt;DOM&lt;/span&gt; id&#8217;s.&lt;/li&gt;
&lt;li&gt;Use acts_as_attachment for handing the storing of files. &lt;/li&gt;
&lt;li&gt;Better validations (aaa will handle that too).&lt;/li&gt;
&lt;li&gt;Rewrite the whole thing.&lt;/li&gt;
&lt;/ul&gt;

	&lt;p&gt;Again, this is a &lt;span class='caps'&gt;VERY&lt;/span&gt; quick-and-dirty demo written in about 20 minutes with so much room for improvement. If I had the time I would, but alas I hope it helps.&lt;/p&gt;
          </content>  </entry>
</feed>
