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

« Newer Snippets
Older Snippets »
Showing 21-30 of 460 total

annotate models with associations

slightly modified http://repo.pragprog.com/svn/Public/plugins/annotate_models/lib/annotate_models.rb to include short declarations for model associations.

require "config/environment"

MODEL_DIR   = File.join(RAILS_ROOT, "app/models")
FIXTURE_DIR = File.join(RAILS_ROOT, "test/fixtures")

module AnnotateModels

  PREFIX = "== Schema Information"
  
  # Simple quoting for the default column value
  def self.quote(value)
    case value
      when NilClass                 then "NULL"
      when TrueClass                then "TRUE"
      when FalseClass               then "FALSE"
      when Float, Fixnum, Bignum    then value.to_s
      # BigDecimals need to be output in a non-normalized form and quoted.
      when BigDecimal               then value.to_s('F')
      else
        value.inspect
    end
  end

  # Use the column information in an ActiveRecord class
  # to create a comment block containing a line for
  # each column. The line contains the column name,
  # the type (and length), and any optional attributes
  def self.get_schema_info(klass, header)
    info = "# == Model #{klass}\n# #{header}\n#\n"
    info << "# Table name: #{klass.table_name}\n#\n"
    
    max_size = klass.column_names.collect{|name| name.size}.max + 1
    klass.columns.each do |col|
      attrs = []
      attrs << "default(#{quote(col.default)})" if col.default
      attrs << "not null" unless col.null
      attrs << "primary key" if col.name == klass.primary_key

      col_type = col.type.to_s
      if col_type == "decimal"
        col_type << "(#{col.precision}, #{col.scale})"
      else
        col_type << "(#{col.limit})" if col.limit
      end 
      info << sprintf("#  %-#{max_size}.#{max_size}s:%-15.15s %s\n", col.name, col_type, attrs.join(", "))
    end
    assoc_list = klass.reflect_on_all_associations.sort {|x,y| d = x.class_name.to_s <=> y.class_name.to_s; c = d == 0 ? x.macro.to_s <=> y.macro.to_s : d; c == 0 ? x.name.to_s <=> y.name.to_s : c }
    unless assoc_list.empty?
      info << "#\n# == Associations\n" 
      assoc_list.each do |assoc|
        ao = assoc.options.dup
        ao.delete(:class_name)
        line = "# * " +sprintf("<tt>%-25s :%-25s (%s) %s</tt>", assoc.macro, assoc.name, assoc.class_name, ao.empty? ? '' : ao.inspect)
        info << line << "\n"
      end 
    end
    info << "#\n\n"
  end

  # Add a schema block to a file. If the file already contains
  # a schema info block (a comment starting
  # with "Schema as of ..."), remove it first.

  def self.annotate_one_file(file_name, info_block)
    if File.exist?(file_name)
      content = File.read(file_name)

      # Remove old schema info
      content.sub!(/^# #{PREFIX}.*?\n(#.*\n)*\n/, '')

      # Write it back
      File.open(file_name, "w") { |f| f.puts info_block + content }
    end
  end
  
  # Given the name of an ActiveRecord class, create a schema
  # info block (basically a comment containing information
  # on the columns and their types) and put it at the front
  # of the model and fixture source files.

  def self.annotate(klass, header)
    info = get_schema_info(klass, header)
    
    model_file_name = File.join(MODEL_DIR, klass.name.underscore + ".rb")
    annotate_one_file(model_file_name, info)

    fixture_file_name = File.join(FIXTURE_DIR, klass.table_name + ".yml")
    annotate_one_file(fixture_file_name, info)
  end

  # Return a list of the model files to annotate. If we have 
  # command line arguments, they're assumed to be either
  # the underscore or CamelCase versions of model names.
  # Otherwise we take all the model files in the 
  # app/models directory.
  def self.get_model_names
    models = ARGV.dup
    models.shift
    
    if models.empty?
      Dir.chdir(MODEL_DIR) do 
        models = Dir["**/*.rb"]
      end
    end
    models
  end

  # We're passed a name of things that might be 
  # ActiveRecord models. If we can find the class, and
  # if its a subclass of ActiveRecord::Base,
  # then pas it to the associated block

  def self.do_annotations
    header = PREFIX.dup
    version = ActiveRecord::Migrator.current_version rescue 0
    if version > 0
      header << "\n# Schema version: #{version}"
    end
    
    self.get_model_names.each do |m|
      class_name = m.sub(/\.rb$/,'').camelize
      begin
        klass = class_name.split('::').inject(Object){ |klass,part| klass.const_get(part) }
        if klass < ActiveRecord::Base && !klass.abstract_class?
          puts "Annotating #{class_name}"
          self.annotate(klass, header)
        else
          puts "Skipping #{class_name}"
        end
      rescue Exception => e
        puts "Unable to annotate #{class_name}: #{e.message}"
      end
      
    end
  end
end

Testing for nil or empty params in Ruby on Rails

I find myself doing these 4 things a lot:
if params[:object] && !params[:object].empty
if params[:object] && params[:object] == value
if params[:object][:attribute] && !params[:object][:attribute].empty
if params[:object][:attribute] && params[:object][:attribute] == value

I put params_check() in my application.rb and it allows me to do this instead:
if params_check(:object)
if params_check(:object, value)
if params_check([:object, :attribute])
if params_check([:object, :attribute], value)

  def params_check(*args)
    if args.length == 1
      if args[0].class == Array
        if params[args[0][0]][args[0][1]] && !params[args[0][0]][args[0][1]].empty?
          true
        end
      else        
        if params[args[0]] && !params[args[0]].empty?
          true
        end
      end
    elsif args.length == 2
      if args[0].class == Array
        if params[args[0][0]][args[0][1]] && params[args[0][0]][args[0][1]] == args[1]
          true
        end
      else
        if params[args[0]] && params[args[0]] == args[1]
          true
        end
      end  
    end
  end

I stole this off another snippet and modified it to add more conditions. Thanks to whoever it was.

Rails helpers for A List Apart No. 256

I cooked up some Ruby on Rails helpers for testing the techniques in the article Accessible Data Visualization with Web Standards from A List Apart No. 256. If you want to know more about Rails, check out No. 257. If you want to be smarter, read A List Apart every week.

def chartlist(data)
  total = data.inject(0.0) { |sum, datum| sum + datum[:count] }
  bars = ''

  data.each do |datum|
    link  = content_tag 'a', datum[:name], :href => datum[:href]
    count = content_tag 'span', datum[:count], :class => 'count'
    index = content_tag 'span', "(#{(datum[:count]/total*100).to_i}%)", :class => 'index', :style => "width: #{(datum[:count]/total*100).to_i}%"
    bars << content_tag('li', link << count << index)
  end

  content_tag 'ul', bars, :class => 'chartlist'
end


Sample data for a chartlist (example)

data = [{ :name => 'Apples',   :count => 420, :href => 'http://www.example.com/fruits/apples/' },
        { :name => 'Bananas',  :count => 280, :href => 'http://www.example.com/fruits/bananas/' },
        { :name => 'Cherries', :count => 200, :href => 'http://www.example.com/fruits/cherries/' },
        { :name => 'Dates',    :count => 100, :href => 'http://www.example.com/fruits/dates/' }]


def sparkline(data)
  max = data.sort.last.to_f
  sparklines = ''

  data.each_with_index do |datum, index|
    count_string = datum.to_s
    '(' << count_string if index == 0
    count_string << ',' if index != data.length
    count_string << ')' if index == data.length
    count = content_tag 'span', count_string, :class => 'count', :style => "height: #{(datum/max*100).to_i}%"
    index = content_tag 'span', count << ' ', :class => 'index'
    sparklines << index
  end

  content_tag('span', sparklines, :class => 'sparkline')
end


Sample data for sparklines (example)

data = [60, 220, 140, 80, 110, 90, 180, 140, 120, 160, 175, 225, 175, 125]


def timeline(data)
  max = data.sort { |a, b| a[:count] <=> b[:count] }.last[:count]
  bars = ''

  data.each do |datum|
    label = content_tag 'span', datum[:label], :class => 'label'
    count = content_tag 'span', "(#{datum[:count]})", :class => 'count', :style => "height: #{(datum[:count]/max.to_f*100).to_i}%"
    link  = content_tag 'a', label << count, :href => datum[:href], :title => "#{datum[:label]}: #{datum[:count]}"
    bars << content_tag('li', link, :style => "width: #{(100.0/data.length).to_i}%")
  end

  content_tag 'ul', bars, :class => 'timeline'
end


Sample data for a timeline (example)

data = [{ :date => '2007-12-01', :count =>  40, label => '1' },
        { :date => '2007-12-02', :count => 100, label => '2' },
        { :date => '2007-12-03', :count => 150, label => '3' }]

rails sendmail settings

config.action_mailer.raise_delivery_errors = true
ActionMailer::Base.delivery_method = :sendmail
ActionMailer::Base.sendmail_settings = {
    :location       => '/usr/sbin/sendmail',
    :arguments      => '-i -t'
}

record grouping

@users = User.find(
  :all, 
  :select => "MONTH(created_at) as month, YEAR(created_at) as year, COUNT(*) AS records",
  :conditions => ["admin = 0'"], 
  :group => "year, month")

<% for @user in @users %>
  <%=@user.month%>
  <%=@user.year%>
  <%=@user.records%>
<% end %>

date_select conversion

function to convert a value from a date_select into a more sql-friendly value

<%=date_select(:date,'',:start_year => 1950,:include_blank => false, :default => { :year => '1970' })%>

def convert_date(obj) 
  return “#{obj[‘(1i)’]}-#{obj[‘(2i)’]}-#{obj[‘(3i)’]}” 
end

ISO 8601 date format for microformats

Strftime to get the ISO 8601 (see RFC 3339) full date format for an hEvent Microformat. Doubtless usable in other situations.

strftime('%Y-%m-%dT%H:%M:%S%z');


If you're in Ruby, you can access this the quick way with:

$ irb
>> require 'time'
=> true
>> Time.now.iso8601
=> "2008-07-09T16:13:30+12:00"


Rails example:

<abbr class="dtstart" title="<%= event.start_date.iso8601"><%= event.start_date.to_s(:long) %></abbr>

Finding recent data in database

This shows how to find recent data from last hour, day, week, month, whatever

class Post < ActiveRecord::Base
  def self.find_latest(time = nil)
    r = %w( hour day week month year )
    if r.include?(time)
      self.find :all, :conditions => ['created_at > ?', 1.send(time).ago]
    else
      self.find :all
    end
  end
end

Post.find_latest('day')
Post.find_latest('week')
Post.find_latest('year')

Simple cacher module for Rails

Illustration of a simple cacher module for Rails.

# lib/cacher.rb
module Cacher
  STORE = {}
  
  def cache(*args)
    STORE[args.inspect] ||= yield
  end

  def flush!
    STORE.clear
  end
  module_function :flush!
end

# app/models/person.rb
class Person < AR::Base
  include Cacher

  def finger
    cache(:finger, email) do
      `finger #{email}`
    end
  end
end

# app/controller/application.rb
class ApplicationController < AC::Base
  after_filter { Cacher.flush! }
end

Default Values for Blank Values OO-style

In Ruby the idiom || can be used to provide a default value if something is nil. This can't be done with blank since an empty string is not nil and will short-circuit the || operator, leaving most of us to to do something like "name.blank? ? 'John Doe' : name". Here's a way to do this OO-style:

class Object  
  def or_if_blank(value)  
    self.respond_to?(:blank) && self.blank? ? value : self  
  end  
end

name.or_if_blank("John Doe")
« Newer Snippets
Older Snippets »
Showing 21-30 of 460 total