Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

About this user

Tim Morgan http://timmorgan.org

« Newer Snippets
Older Snippets »
Showing 1-10 of 13 total  RSS 

Automatic Expiration of Rails Action Caching

Would love to get some feedback on this...

No doubt, 5 minutes after posting this, someone will tell me of the built-in Rails way of doing this, but alas I could not find it.

Usage looks like this:

   1  
   2  class PeopleController < ApplicationController
   3    caches_action :show, :for => 1.hour, :cache_path => Proc.new { |c| "people/#{c.params[:id]}_for_#{Person.logged_in.id}" }
   4  end


The ":for => 1.hour" part is where the magic happens.

Basically, this bit of code adds a before_filter that checks the last modified time of the cache entry, and expires it if it is older than the specified time period.

   1  
   2  module ActionController
   3    module Caching
   4      module Fragments
   5        # expire a cache key only if the block returns true or
   6        # if the age of the fragment is more than the specified age argument.
   7        def expire_fragment_by_mtime(key, age=nil, &block)
   8          block = Proc.new { |m| m < age.ago } unless block_given?
   9          if (m = cache_store.mtime(fragment_cache_key(key))) and block.call(m)
  10            expire_fragment(key)
  11          end
  12        end
  13      end
  14      module Actions
  15        module ClassMethods
  16          # adds an option :for
  17          # caches_action :show, :for => 2.hours, :cache_path => ...
  18          # :cache_path is required, unfortunately
  19          def caches_action_with_for(*actions)
  20            original_actions = actions.clone
  21            options = actions.extract_options!
  22            if for_time = options.delete(:for)
  23              cache_path = options[:cache_path]
  24              before_filter do |controller|
  25                cache_path = cache_path.call(controller) if cache_path.respond_to?(:call)
  26                controller.expire_fragment_by_mtime(cache_path, for_time)
  27              end
  28            end
  29            caches_action_without_for(*original_actions)
  30          end
  31          alias_method_chain :caches_action, :for
  32        end
  33      end
  34    end
  35  end
  36  
  37  # Add a method to grab the last modified time of the cache key.
  38  # If you use a store other than the FileStore, you'll need to add
  39  # a method like this to your store.
  40  module ActiveSupport
  41    module Cache
  42      class FileStore
  43        def mtime(name)
  44          File.mtime(real_file_path(name)) rescue nil
  45        end
  46      end
  47    end
  48  end

Rails Notice/Warning Flash Message

Somewhat lame, but handy nonetheless.

   1  
   2  <% if flash[:warning] or flash[:notice] %>
   3    <div id="notice" <% if flash[:warning] %>class="warning"<% end %>>
   4      <%= flash[:warning] || flash[:notice] %>
   5    </div>
   6    <script type="text/javascript">
   7      setTimeout("new Effect.Fade('notice');", 15000)
   8    </script>
   9  <% end %>

Rails MySQL/SQLite convenience methods

Usage:

   1  
   2    Person.find :all, :conditions => ["#{sql_year 'birthday'} >= ?", year]


   1  
   2  SQLITE = true # or false
   3  
   4  def sql_concat(*args)
   5    SQLITE ? args.join(' || ') : "CONCAT(#{args.join(', ')})"
   6  end
   7  
   8  def sql_lcase(expr)
   9    SQLITE ? "LOWER(#{expr})" : "LCASE(#{expr})"
  10  end
  11  
  12  def sql_year(expr)
  13    SQLITE ? "CAST(STRFTIME('%y', #{expr}) as 'INTEGER')" : "YEAR(#{expr})"
  14  end
  15  
  16  def sql_month(expr)
  17    SQLITE ? "CAST(STRFTIME('%m', #{expr}) as 'INTEGER')" : "MONTH(#{expr})"
  18  end
  19  
  20  def sql_day(expr)
  21    SQLITE ? "CAST(STRFTIME('%d', #{expr}) as 'INTEGER')" : "DAY(#{expr})"
  22  end
  23  
  24  def sql_now
  25    SQLITE ? "CURRENT_TIMESTAMP" : "NOW()"
  26  end

Rails Array#add_condition Method

Lets you add a condition to a set of ActiveRecord conditions easily like this:

   1  
   2    conditions = ['active = ? and type = ?', true, 2]
   3    conditions.add_condition ['person_id = ?', 345]


   1  
   2  class Array
   3    def add_condition(condition, conjunction='and')
   4      if condition.is_a? Array
   5        if self.empty?
   6          (self << condition).flatten!
   7        else
   8          self[0] += " #{conjunction} " + condition.shift
   9          (self << condition).flatten!
  10        end
  11      elsif condition.is_a? String
  12        self[0] += " #{conjunction} " + condition
  13      else
  14        raise "don't know how to handle this condition type"
  15      end
  16      self
  17    end
  18  end

Ultimate Radiant CMS Script

This is a script that builds a Radiant CMS site with several third-party extensions I use a lot. This just saves me time from having to look up the setup commands each time. YMMV

I call this power_radiant and stick it in /usr/local/bin.

Update: this has been fixed to work with latest Radiant code (since svn path as changed and freeze:edge task no longer works). You will need Git to be installed on your machine for this to work.

   1  
   2  #!/bin/sh
   3  radiant --database sqlite3 $1
   4  cd $1
   5  echo "
   6  production:
   7    adapter: sqlite3
   8    database: db/production.sqlite3
   9  test:
  10    adapter: sqlite3
  11    database: db/test.sqlite3
  12  development:
  13    production
  14  " > config/database.yml
  15  svn export http://svn.radiantcms.org/radiant/trunk/extensions/shards/ vendor/extensions/shards
  16  svn export svn://zuurstof.openminds.be/home/kaizer/svn/rails_stuff/radiant_extensions/wym_editor_filter vendor/extensions/wym_editor_filter
  17  svn export http://svn.seancribbs.com/svn/rails/plugins/extensions/page_attachments vendor/extensions/page_attachments
  18  #rake radiant:freeze:edge # stopped working in latest gem
  19  git clone git://github.com/seancribbs/radiant.git /tmp/radiant
  20  cp -r /tmp/radiant/radiant vendor/radiant
  21  rake production db:bootstrap
  22  rake production db:migrate:extensions
  23  rake production radiant:extensions:wym_editor_filter:install
  24  rake production radiant:extensions:page_attachments:update


To run:
   1  power_radiant my_site

Using POP3 to Retrieve Email for Rails App

Put this in a file called "inbox" in the scripts/ directory of your Rails app. change the host, username, and password to match.

Set up a cron job to run this script every so often (frequency depends on your needs).

   1  
   2  #!/usr/bin/env ruby
   3  
   4  require 'net/pop'
   5  require File.dirname(__FILE__) + '/../config/environment'
   6  
   7  logger = RAILS_DEFAULT_LOGGER
   8  
   9  logger.info "Running Mail Importer..." 
  10  Net::POP3.start("localhost", nil, "username", "password") do |pop|
  11    if pop.mails.empty?
  12      logger.info "NO MAIL" 
  13    else
  14      pop.mails.each do |email|
  15        begin
  16          logger.info "receiving mail..." 
  17          Notifier.receive(email.pop)
  18          email.delete
  19        rescue Exception => e
  20          logger.error "Error receiving email at " + Time.now.to_s + "::: " + e.message
  21        end
  22      end
  23    end
  24  end
  25  logger.info "Finished Mail Importer." 
  26  

image_tag with popup for alt attribute

Firefox doesn't display a popup for the alt attribute for images. While this is according to spec, it's slightly annoying to put the same text in both the alt and the title. Put the following in your ApplicationHelper to copy the alt to the title for every image. Please don't tell me how wrong this is. I don't care.

   1  
   2  module ApplicationHelper
   3    def image_tag(location, options)
   4      options[:title] ||= options[:alt]
   5      super(location, options)
   6    end
   7  end

Fix for ActiveRecord SQL Server adapter dates

The SQL Server adapter for ActiveRecord uses Time objects to cast dates from the db. This fails for dates before 1970, thus some birthdates come back as nil. This is some kludge to use a DateTime in that case so we still get the value.

A better approach may be to convert this code to use DateTime objects exclusively, but I'm not sure of the speed implications of doing so. The code below first tries to cast the value to a Time object; if that fails, it tries a DateTime object; if that fails, it returns nil.

Stick this in a plugin to use with Rails.

   1  
   2  module ActiveRecord
   3    module ConnectionAdapters
   4      class ColumnWithIdentity
   5        def cast_to_time(value)
   6          return value if value.is_a?(Time) or value.is_a?(DateTime)
   7          time_array = ParseDate.parsedate(value)
   8          time_array[0] ||= 2000
   9          time_array[1] ||= 1
  10          time_array[2] ||= 1
  11          Time.send(Base.default_timezone, *time_array) rescue DateTime.new(*time_array[0..5]) rescue nil
  12        end
  13        def cast_to_datetime(value)
  14          if value.is_a?(Time) or value.is_a?(DateTime)
  15            if value.year != 0 and value.month != 0 and value.day != 0
  16              return value
  17            else
  18              return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
  19            end
  20          end
  21          return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
  22          value
  23        end
  24      end
  25    end
  26  end

Force output to download as a file in Rails

   1  
   2  response.headers['Content-Type'] = 'text/csv' # I've also seen this for CSV files: 'text/csv; charset=iso-8859-1; header=present'
   3  response.headers['Content-Disposition'] = 'attachment; filename=thefile.csv'

Rails - Easily work with uppercase column names

I had to work with a legacy database with hundreds of tables with uppercase column names. Here is how I made my life a whole lot easier...

   1  
   2  module ActiveRecord
   3    class MyCustomARClass < ActiveRecord::Base
   4    
   5      def self.reloadable?
   6        false
   7      end
   8      
   9      # all columns are uppercase
  10      set_primary_key 'ID'
  11      
  12      # convert to uppercase attribute if in existence
  13      # record.name => record.NAME
  14      # record.id => record.ID
  15      def method_missing(method_id, *args, &block)
  16        method_id = method_id.to_s.upcase if @attributes.include? method_id.to_s.upcase.gsub(/=/, '')
  17        super(method_id, *args, &block)
  18      end
  19  
  20      # strip leading and trailing spaces from attribute string values
  21      def read_attribute(attr_name)
  22        value = super(attr_name)
  23        value.is_a?(String) ? value.strip : value
  24      end
  25  
  26      class << self # Class methods
  27        
  28        private
  29          # allow for find_by and such to work with uppercase attributes
  30          # find_by_name => find_by_NAME
  31          # find_by_dob_and_height => find_by_DOB_and_HEIGHT
  32          def extract_attribute_names_from_match(match)
  33            match.captures.last.split('_and_').map { |name| name.upcase }
  34          end
  35      end
  36    end
  37  end


This was defined in a plugin. Then, in each of my models I used the subclass instead of ActiveRecord::Base, like so...

   1  
   2  class MyModel < ActiveRecord::MyCustomARClass
   3    # ...
   4  end
« Newer Snippets
Older Snippets »
Showing 1-10 of 13 total  RSS