<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: activerecord code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Thu, 24 Jul 2008 00:11:20 GMT</pubDate>
    <description>DZone Snippets: activerecord 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>
  </channel>
</rss>
