Skip to content

Commit

Permalink
Merge branch 'master' of git://github.com/JackDanger/annotate_models …
Browse files Browse the repository at this point in the history
…into JackDanger/master
  • Loading branch information
Cuong Tran committed Mar 10, 2008
2 parents da0a684 + 6713e92 commit 4012cbe
Showing 1 changed file with 112 additions and 100 deletions.
212 changes: 112 additions & 100 deletions lib/annotate_models.rb
Original file line number Diff line number Diff line change
@@ -1,123 +1,135 @@
module AnnotateModels
MODEL_DIR = "app/models"
FIXTURE_DIR = "test/fixtures"
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
class << self
MODEL_DIR = "app/models"
FIXTURE_DIR = "test/fixtures"
PREFIX = "== Schema Information"

# Simple quoting for the default column value
def 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
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 = "# #{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
# 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 get_schema_info(klass, header)
info = "# #{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

info << "#\n\n"
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.
# 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)
def 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/, '')
# Remove old schema info
content.sub!(/^# #{PREFIX}.*?\n(#.*\n)*\n/, '')

# Write it back
File.open(file_name, "w") { |f| f.puts info_block + content }
# Write it back
File.open(file_name, "w") { |f| f.puts info_block + content }
end
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.
# 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)
def annotate(klass, file, 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)
model_file_name = File.join(MODEL_DIR, file)
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
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"]
# 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 get_model_files
models = ARGV.dup
models.shift

if models.empty?
Dir.chdir(MODEL_DIR) do
models = Dir["**/*.rb"]
end
end
models
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}"

# Retrieve the classes belonging to the model names we're asked to process
# Check for namespaced models in subdirectories as well as models
# in subdirectories without namespacing.
def get_model_class(file)
model = file.gsub(/\.rb$/, '').camelize
parts = model.split('::')
begin
parts.inject(Object) {|klass, part| klass.const_get(part) }
rescue LoadError
Object.const_get(parts.last)
end
end

annotated = []
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?
annotated << class_name
self.annotate(klass, header)
end
rescue Exception => e
puts "Unable to annotate #{class_name}: #{e.message}"
# 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 do_annotations
header = PREFIX.dup
version = ActiveRecord::Migrator.current_version rescue 0
if version > 0
header << "\n# Schema version: #{version}"
end

annotated = []
get_model_files.each do |file|
begin
klass = get_model_class(file)
if klass < ActiveRecord::Base && !klass.abstract_class?
annotated << klass
annotate(klass, file, header)
end
rescue Exception => e
puts "Unable to annotate #{file}: #{e.message}"
end
end
puts "Annotated #{annotated.join(', ')}"
end
puts "Annotated #{annotated.join(', ')}"
end
end

0 comments on commit 4012cbe

Please sign in to comment.