<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: rails code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Sun, 27 Jul 2008 04:10:12 GMT</pubDate>
    <description>DZone Snippets: rails code</description>
    <item>
      <title>annotate models with associations</title>
      <link>http://snippets.dzone.com/posts/show/5456</link>
      <description>slightly modified http://repo.pragprog.com/svn/Public/plugins/annotate_models/lib/annotate_models.rb to include short declarations for model associations.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;require "config/environment"&lt;br /&gt;&lt;br /&gt;MODEL_DIR   = File.join(RAILS_ROOT, "app/models")&lt;br /&gt;FIXTURE_DIR = File.join(RAILS_ROOT, "test/fixtures")&lt;br /&gt;&lt;br /&gt;module AnnotateModels&lt;br /&gt;&lt;br /&gt;  PREFIX = "== Schema Information"&lt;br /&gt;  &lt;br /&gt;  # Simple quoting for the default column value&lt;br /&gt;  def self.quote(value)&lt;br /&gt;    case value&lt;br /&gt;      when NilClass                 then "NULL"&lt;br /&gt;      when TrueClass                then "TRUE"&lt;br /&gt;      when FalseClass               then "FALSE"&lt;br /&gt;      when Float, Fixnum, Bignum    then value.to_s&lt;br /&gt;      # BigDecimals need to be output in a non-normalized form and quoted.&lt;br /&gt;      when BigDecimal               then value.to_s('F')&lt;br /&gt;      else&lt;br /&gt;        value.inspect&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # Use the column information in an ActiveRecord class&lt;br /&gt;  # to create a comment block containing a line for&lt;br /&gt;  # each column. The line contains the column name,&lt;br /&gt;  # the type (and length), and any optional attributes&lt;br /&gt;  def self.get_schema_info(klass, header)&lt;br /&gt;    info = "# == Model #{klass}\n# #{header}\n#\n"&lt;br /&gt;    info &lt;&lt; "# Table name: #{klass.table_name}\n#\n"&lt;br /&gt;    &lt;br /&gt;    max_size = klass.column_names.collect{|name| name.size}.max + 1&lt;br /&gt;    klass.columns.each do |col|&lt;br /&gt;      attrs = []&lt;br /&gt;      attrs &lt;&lt; "default(#{quote(col.default)})" if col.default&lt;br /&gt;      attrs &lt;&lt; "not null" unless col.null&lt;br /&gt;      attrs &lt;&lt; "primary key" if col.name == klass.primary_key&lt;br /&gt;&lt;br /&gt;      col_type = col.type.to_s&lt;br /&gt;      if col_type == "decimal"&lt;br /&gt;        col_type &lt;&lt; "(#{col.precision}, #{col.scale})"&lt;br /&gt;      else&lt;br /&gt;        col_type &lt;&lt; "(#{col.limit})" if col.limit&lt;br /&gt;      end &lt;br /&gt;      info &lt;&lt; sprintf("#  %-#{max_size}.#{max_size}s:%-15.15s %s\n", col.name, col_type, attrs.join(", "))&lt;br /&gt;    end&lt;br /&gt;    assoc_list = klass.reflect_on_all_associations.sort {|x,y| d = x.class_name.to_s &lt;=&gt; y.class_name.to_s; c = d == 0 ? x.macro.to_s &lt;=&gt; y.macro.to_s : d; c == 0 ? x.name.to_s &lt;=&gt; y.name.to_s : c }&lt;br /&gt;    unless assoc_list.empty?&lt;br /&gt;      info &lt;&lt; "#\n# == Associations\n" &lt;br /&gt;      assoc_list.each do |assoc|&lt;br /&gt;        ao = assoc.options.dup&lt;br /&gt;        ao.delete(:class_name)&lt;br /&gt;        line = "# * " +sprintf("&lt;tt&gt;%-25s :%-25s (%s) %s&lt;/tt&gt;", assoc.macro, assoc.name, assoc.class_name, ao.empty? ? '' : ao.inspect)&lt;br /&gt;        info &lt;&lt; line &lt;&lt; "\n"&lt;br /&gt;      end &lt;br /&gt;    end&lt;br /&gt;    info &lt;&lt; "#\n\n"&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # Add a schema block to a file. If the file already contains&lt;br /&gt;  # a schema info block (a comment starting&lt;br /&gt;  # with "Schema as of ..."), remove it first.&lt;br /&gt;&lt;br /&gt;  def self.annotate_one_file(file_name, info_block)&lt;br /&gt;    if File.exist?(file_name)&lt;br /&gt;      content = File.read(file_name)&lt;br /&gt;&lt;br /&gt;      # Remove old schema info&lt;br /&gt;      content.sub!(/^# #{PREFIX}.*?\n(#.*\n)*\n/, '')&lt;br /&gt;&lt;br /&gt;      # Write it back&lt;br /&gt;      File.open(file_name, "w") { |f| f.puts info_block + content }&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  # Given the name of an ActiveRecord class, create a schema&lt;br /&gt;  # info block (basically a comment containing information&lt;br /&gt;  # on the columns and their types) and put it at the front&lt;br /&gt;  # of the model and fixture source files.&lt;br /&gt;&lt;br /&gt;  def self.annotate(klass, header)&lt;br /&gt;    info = get_schema_info(klass, header)&lt;br /&gt;    &lt;br /&gt;    model_file_name = File.join(MODEL_DIR, klass.name.underscore + ".rb")&lt;br /&gt;    annotate_one_file(model_file_name, info)&lt;br /&gt;&lt;br /&gt;    fixture_file_name = File.join(FIXTURE_DIR, klass.table_name + ".yml")&lt;br /&gt;    annotate_one_file(fixture_file_name, info)&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # Return a list of the model files to annotate. If we have &lt;br /&gt;  # command line arguments, they're assumed to be either&lt;br /&gt;  # the underscore or CamelCase versions of model names.&lt;br /&gt;  # Otherwise we take all the model files in the &lt;br /&gt;  # app/models directory.&lt;br /&gt;  def self.get_model_names&lt;br /&gt;    models = ARGV.dup&lt;br /&gt;    models.shift&lt;br /&gt;    &lt;br /&gt;    if models.empty?&lt;br /&gt;      Dir.chdir(MODEL_DIR) do &lt;br /&gt;        models = Dir["**/*.rb"]&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    models&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # We're passed a name of things that might be &lt;br /&gt;  # ActiveRecord models. If we can find the class, and&lt;br /&gt;  # if its a subclass of ActiveRecord::Base,&lt;br /&gt;  # then pas it to the associated block&lt;br /&gt;&lt;br /&gt;  def self.do_annotations&lt;br /&gt;    header = PREFIX.dup&lt;br /&gt;    version = ActiveRecord::Migrator.current_version rescue 0&lt;br /&gt;    if version &gt; 0&lt;br /&gt;      header &lt;&lt; "\n# Schema version: #{version}"&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    self.get_model_names.each do |m|&lt;br /&gt;      class_name = m.sub(/\.rb$/,'').camelize&lt;br /&gt;      begin&lt;br /&gt;        klass = class_name.split('::').inject(Object){ |klass,part| klass.const_get(part) }&lt;br /&gt;        if klass &lt; ActiveRecord::Base &amp;&amp; !klass.abstract_class?&lt;br /&gt;          puts "Annotating #{class_name}"&lt;br /&gt;          self.annotate(klass, header)&lt;br /&gt;        else&lt;br /&gt;          puts "Skipping #{class_name}"&lt;br /&gt;        end&lt;br /&gt;      rescue Exception =&gt; e&lt;br /&gt;        puts "Unable to annotate #{class_name}: #{e.message}"&lt;br /&gt;      end&lt;br /&gt;      &lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Sat, 03 May 2008 11:05:17 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/5456</guid>
      <author>dseverin ()</author>
    </item>
    <item>
      <title>PgProc - call PostgreSQL functions from Rails app</title>
      <link>http://snippets.dzone.com/posts/show/1008</link>
      <description>&lt;code&gt;&lt;br /&gt;# Just for fun and horror: make database functions a part of your Rails model! :)&lt;br /&gt;#&lt;br /&gt;# Class to access to PostgreSQL functions. Returned value depends on params and query result, see below.&lt;br /&gt;#&lt;br /&gt;# Currently supported options are:&lt;br /&gt;#     :order =&gt; '1 desc' # to add order clause&lt;br /&gt;#     :use_from =&gt; true  # to add "* from" for non-model function queries, which return records&lt;br /&gt;#     :all =&gt; true       # to return not first but all found models&lt;br /&gt;#     :cast =&gt; string    # to cast result (useful for functions, returning +setof record+)&lt;br /&gt;#&lt;br /&gt;# Call-patterns:&lt;br /&gt;#&lt;br /&gt;# A) Model loading from functions that return setof system known rowtype&lt;br /&gt;#     PgProc.function(ModelClass[, options])&lt;br /&gt;#         PgProc.get_descendants(ContentNode, 123)&lt;br /&gt;#&lt;br /&gt;#     PgProc.function(ModelClass, value[, options ])&lt;br /&gt;#         PgProc.get_children(ContentNode, 123, :order =&gt; 'position', :all =&gt; true)&lt;br /&gt;#&lt;br /&gt;# returns either:&lt;br /&gt;# * empty array if nothing found&lt;br /&gt;# * first found model object, if found only one and +:all+ option is not set&lt;br /&gt;# * array of model objects&lt;br /&gt;#&lt;br /&gt;# B) Values from functions&lt;br /&gt;#&lt;br /&gt;# PgProc.function(:type_symbol, value, [type_symbol2, value2, ...[, options]]) - for explicit parameter typecast&lt;br /&gt;#     PgProc.array_append(:"int[]", '{1,2,3,4}', :int, 5) # =&gt; {1,2,3,4,5}&lt;br /&gt;#&lt;br /&gt;# PgProc.function(*args [, options])&lt;br /&gt;#     PgProc.generate_series(1,10,2, :order =&gt; '1 desc') # =&gt; [9,5,7,3,2,1]&lt;br /&gt;#&lt;br /&gt;# PgProc.function() - for functions w/o params&lt;br /&gt;#     PgProc.now()&lt;br /&gt;#&lt;br /&gt;# returns either:&lt;br /&gt;# * empty string for +void+ functions&lt;br /&gt;# * single value, if resultset has 1x1 dimension&lt;br /&gt;# * array of values if resultset has Nx1 dimension (N&gt;1)&lt;br /&gt;# * array of rows otherwise&lt;br /&gt;#&lt;br /&gt;# Throws PGError, if function doesn't exist or wrong params supplied&lt;br /&gt;&lt;br /&gt;class PgProc &lt; ActiveRecord::Base&lt;br /&gt;    set_table_name 'pg_catalog.pg_proc'&lt;br /&gt;    set_primary_key 'oid'&lt;br /&gt;    def readonly?&lt;br /&gt;      true&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;private&lt;br /&gt;    def self.method_missing(meth_sym, *args)&lt;br /&gt;      func_name = meth_sym.id2name&lt;br /&gt;      super unless find(:first, :conditions =&gt; ['proname = ?', func_name])&lt;br /&gt;      if ! args.empty? &amp;&amp; args.last.is_a?(Hash)&lt;br /&gt;        options = args.pop&lt;br /&gt;        order_str = " ORDER BY #{options[:order]}" if options[:order]&lt;br /&gt;      else&lt;br /&gt;        options = {}&lt;br /&gt;        order_str = nil&lt;br /&gt;      end&lt;br /&gt;      from_str = " * FROM " if options[:use_from]&lt;br /&gt;      if args.empty?&lt;br /&gt;        temp = connection.query("select #{from_str} #{func_name}() #{options[:cast]} #{order_str}")&lt;br /&gt;      elsif args.first.is_a?(Class)&lt;br /&gt;        model_klass = args.shift&lt;br /&gt;        if args.length == 0&lt;br /&gt;          temp = model_klass.find_by_sql("select * from  #{func_name}()  #{options[:cast]} #{order_str}")&lt;br /&gt;        else&lt;br /&gt;          temp = model_klass.find_by_sql("select * from  #{func_name}(#{quote_bound_value(args)})  #{options[:cast]} #{order_str}")&lt;br /&gt;        end&lt;br /&gt;        return temp if options[:all]&lt;br /&gt;        return temp.length == 1 ? temp.first : temp&lt;br /&gt;      else&lt;br /&gt;          if args.length % 2 == 0 &amp;&amp; args.first.is_a?(Symbol)&lt;br /&gt;            temp = connection.query("select #{from_str} #{func_name}(#{quote_bound_value_types(args)}) #{options[:cast]} #{order_str}")&lt;br /&gt;          else&lt;br /&gt;            temp = connection.query("select #{from_str} #{func_name}(#{quote_bound_value(args)})  #{options[:cast]} #{order_str}")&lt;br /&gt;          end&lt;br /&gt;      end&lt;br /&gt;      return temp.first.first if temp.length == 1 &amp;&amp; temp.first.length == 1&lt;br /&gt;      return temp.flatten if temp.length &gt; 1 &amp;&amp; temp.first.length == 1&lt;br /&gt;      return temp&lt;br /&gt;    end&lt;br /&gt;    def self.quote_bound_value_types(value)&lt;br /&gt;      i = true&lt;br /&gt;      value.partition {|v| i = !i }.transpose.map{|v| "#{connection.quote(v[0])}::#{v[1]}"}.join(',')&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Thu, 22 Dec 2005 18:52:12 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/1008</guid>
      <author>dseverin ()</author>
    </item>
  </channel>
</rss>
