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 54 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:

class PeopleController < ApplicationController
  caches_action :show, :for => 1.hour, :cache_path => Proc.new { |c| "people/#{c.params[:id]}_for_#{Person.logged_in.id}" }
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.

module ActionController
  module Caching
    module Fragments
      # expire a cache key only if the block returns true or
      # if the age of the fragment is more than the specified age argument.
      def expire_fragment_by_mtime(key, age=nil, &block)
        block = Proc.new { |m| m < age.ago } unless block_given?
        if (m = cache_store.mtime(fragment_cache_key(key))) and block.call(m)
          expire_fragment(key)
        end
      end
    end
    module Actions
      module ClassMethods
        # adds an option :for
        # caches_action :show, :for => 2.hours, :cache_path => ...
        # :cache_path is required, unfortunately
        def caches_action_with_for(*actions)
          original_actions = actions.clone
          options = actions.extract_options!
          if for_time = options.delete(:for)
            cache_path = options[:cache_path]
            before_filter do |controller|
              cache_path = cache_path.call(controller) if cache_path.respond_to?(:call)
              controller.expire_fragment_by_mtime(cache_path, for_time)
            end
          end
          caches_action_without_for(*original_actions)
        end
        alias_method_chain :caches_action, :for
      end
    end
  end
end

# Add a method to grab the last modified time of the cache key.
# If you use a store other than the FileStore, you'll need to add
# a method like this to your store.
module ActiveSupport
  module Cache
    class FileStore
      def mtime(name)
        File.mtime(real_file_path(name)) rescue nil
      end
    end
  end
end

nslookup.rb

Does a web-based DNS lookup. Useful for those of us behind corporate firewalls that block fun sites based on DNS queries, i.e. OpenDNS.

#!/usr/bin/env ruby

LOOKUP_URL = 'http://toolbar.netcraft.com/site_report?url=%s'
MATCH_RE = /<td><b>IP address<\/b><\/td><td.*?>([\d\.]+)<\/td>/
HOSTS_FILE_PATH = '/etc/hosts'

require 'open-uri'

host = ARGV.select { |arg| arg !~ /^\-/ }.first
update_hosts_file = ARGV.select { |arg| arg == '--hosts' }.any?

if open(LOOKUP_URL % host).read =~ MATCH_RE
  ip = $1
  puts ip
  if update_hosts_file
    File.open(HOSTS_FILE_PATH, 'a') do |file|
      file.write("\n#{ip} #{host}")
    end
  end
else
  puts 'There was an error looking up this host.'
end


Usage:

./nslookup.rb youtube.com


To add the entry to your hosts file:

sudo ./nslookup.rb youtube.com --hosts

Trac Wiki to GitHub Wiki

A rough start of a script to help convert the wiki syntax of Trac pages to GitHub-friendly syntax.

Some TLC is still needed on each output page, but better than doing it all by hand.

Project lives here: github.com/seven1m/trac_wiki_to_github

#!/usr/bin/env ruby
 
TRAC_DB_PATH = 'trac.db'
OUT_PATH = 'wiki'
GITHUB_WIKI_URL = '/seven1m/onebody/wikis/'
 
require 'sqlite3'
 
db = SQLite3::Database.new(TRAC_DB_PATH)
pages = db.execute('select name, text from wiki w2 where version = (select max(version) from wiki where name = w2.name);')
 
pages.each do |title, body|
  File.open(File.join(OUT_PATH, title.gsub(/\s/, '')), 'w') do |file|
    body.gsub!(/\{\{\{([^\n]+?)\}\}\}/, '<code>\1</' + 'code>')
    body.gsub!(/\{\{\{(.+?)\}\}\}/m, '<pre><code>\1</' + 'code></pre>')
    body.gsub!(/====\s(.+?)\s====/, 'h4. \1')
    body.gsub!(/===\s(.+?)\s===/, 'h3. \1')
    body.gsub!(/==\s(.+?)\s==/, 'h2. \1')
    body.gsub!(/=\s(.+?)\s=[\s\n]*/, '')
    body.gsub!(/\[(http[^\s\[\]]+)\s([^\[\]]+)\]/, '"\2":\1')
    body.gsub!(/\[([^\s]+)\s(.+)\]/, '"\2":' + GITHUB_WIKI_URL + '\1')
    body.gsub!(/([^"\/\!])(([A-Z][a-z0-9]+){2,})/, '\1[[\2]]')
    body.gsub!(/\!(([A-Z][a-z0-9]+){2,})/, '\1')
    body.gsub!(/'''(.+)'''/, '*\1*')
    body.gsub!(/''(.+)''/, '_\1_')
    body.gsub!(/^\s\*/, '*')
    body.gsub!(/^\s\d\./, '#')
    file.write(body)
  end
end

Rails Notice/Warning Flash Message

Somewhat lame, but handy nonetheless.

<% if flash[:warning] or flash[:notice] %>
  <div id="notice" <% if flash[:warning] %>class="warning"<% end %>>
    <%= flash[:warning] || flash[:notice] %>
  </div>
  <script type="text/javascript">
    setTimeout("new Effect.Fade('notice');", 15000)
  </script>
<% end %>

Copy Public Key To Host In One Line

ssh username@host "echo `cat ~/.ssh/id_dsa.pub` >> ~/.ssh/authorized_keys"

Ruby Array#every method

Chunks an array into smaller arrays of the specified size.

Updated: Turns out a similar method is available in Rails called Array#in_groups_of. But still, I will leave this up because it seems elegant enough to me.

class Array
  def every(count)
    chunks = []
    each_with_index do |item, index|
      chunks << [] if index % count == 0
      chunks.last << item
    end
    chunks
  end
  alias / every
end


Usage:

(1..7).to_a.every(2)
# => [[1, 2], [3, 4], [5, 6], [7]]

(1..7).to_a / 3
# => [[1, 2, 3], [4, 5, 6], [7]]

Rails MySQL/SQLite convenience methods

Usage:

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


SQLITE = true # or false

def sql_concat(*args)
  SQLITE ? args.join(' || ') : "CONCAT(#{args.join(', ')})"
end

def sql_lcase(expr)
  SQLITE ? "LOWER(#{expr})" : "LCASE(#{expr})"
end

def sql_year(expr)
  SQLITE ? "CAST(STRFTIME('%y', #{expr}) as 'INTEGER')" : "YEAR(#{expr})"
end

def sql_month(expr)
  SQLITE ? "CAST(STRFTIME('%m', #{expr}) as 'INTEGER')" : "MONTH(#{expr})"
end

def sql_day(expr)
  SQLITE ? "CAST(STRFTIME('%d', #{expr}) as 'INTEGER')" : "DAY(#{expr})"
end

def sql_now
  SQLITE ? "CURRENT_TIMESTAMP" : "NOW()"
end

Rails Array#add_condition Method

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

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


class Array
  def add_condition(condition, conjunction='and')
    if condition.is_a? Array
      if self.empty?
        (self << condition).flatten!
      else
        self[0] += " #{conjunction} " + condition.shift
        (self << condition).flatten!
      end
    elsif condition.is_a? String
      self[0] += " #{conjunction} " + condition
    else
      raise "don't know how to handle this condition type"
    end
    self
  end
end

Marshalize (Cache) ActiveRecord Query Results

A quick way to cache results in a file and read from the file on subsequent requests instead of the database. Makes the initial query a bit slower, but later queries *much* faster.

class MyCachedModel < ActiveRecord::Base
  class << self
    alias_method :rails_original_find_by_sql, :find_by_sql
    def find_by_sql(sql)
      cache_filename = Base64.encode64(sql)
      if File.exists? cache_filename
        Marshal.load(File.open(cache_filename))
      else
        Marshal.dump(records = rails_original_find_by_sql(sql), File.open(cache_filename, 'w'))
        return records
      end
    end
  end
end

Ruby DBF Library Fixes

A handful of fixes/enhancements for the "dbf" Rubygem.

# example.rb
require 'rubygems'
require 'dbf'
require 'dbf_fixes'

table = DBF::Table.new('/path/to/table.dbf', :in_memory => false)
table.each_record do |record|
  # do something here
  # with in_memory=>false tables, each_record is much more efficient
  # because it reads directly from disk 
end



# dbf_fixes.rb
module DBF
  class Record
    private

    # fix bug in DBF code (or workaround bug in FoxPro dbf files; I don't know :-)
    def initialize_values(columns)
      columns.each do |column|
        case column.type
        when 'I' # added by Tim - I don't understand this much, but it seems to work
          @attributes[column.name] = @data.read(column.length).unpack("I").first
        when 'N' # number
          @attributes[column.name] = column.decimal.zero? ? unpack_string(column).to_i : unpack_string(column).to_f
        when 'D' # date
          raw = unpack_string(column).strip
          unless raw.empty?
            begin
              parts = raw.match(DATE_REGEXP).to_a.slice(1,3).map {|n| n.to_i}
              @attributes[column.name] = Time.gm(*parts)
            rescue
              parts = raw.match(DATE_REGEXP).to_a.slice(1,3).map {|n| n.to_i}
              @attributes[column.name] = Date.new(*parts)
            end
          end
        when 'M' # memo
          starting_block = unpack_string(column).to_i
          @attributes[column.name] = read_memo(starting_block)
        when 'L' # logical
          @attributes[column.name] = unpack_string(column) =~ /^(y|t)$/i ? true : false
        else
          @attributes[column.name] = unpack_string(column).strip
        end
      end
    end

    # don't know why, but accessors stopped working for me.
    def define_accessors
      @table.columns.each do |column|
        underscored_column_name = underscore(column.name)
        if @table.options[:accessors]
          self.class.send :define_method, underscored_column_name do
            @attributes[column.name]
          end
          @@accessors_defined = true
        end
      end
    end
  end

  class Table
    # more efficient iterator (so we don't load everything)
    def each_record
      if options[:in_memory] and @records
        @records.each { |r| yield(r) }
      else
        0.upto(@record_count - 1) do |n|
          seek_to_record(n)
          yield(DBF::Record.new(self)) unless deleted_record?
        end
      end
    end
  end
end
« Newer Snippets
Older Snippets »
Showing 1-10 of 54 total  RSS