Commit d21c1526 by Cuong Tran Committed by Martin Stannard
parent 402ef293
pkg .DS_Store
doc doc/*
rdoc/*
coverage/*
spec/debug.log
pkg/*
== 2.0 2009-02-03
* Add annotate_models plugin fork additions
* Annotates Rspec and Test Unit models
* Annotates Object Daddy exemplars
* Annotates geometrical columns
* Add AnnotateRoutes rake task
* Up gem structure to newgem defaults
== 1.0.4 2008-09-04 == 1.0.4 2008-09-04
* Only update modified models since last run, thanks to sant0sk1 * Only update modified models since last run, thanks to sant0sk1
......
History.txt History.txt
License.txt License.txt
Manifest.txt Manifest.txt
README.txt README.rdoc
Rakefile Rakefile
config/hoe.rb annotate.gemspec
config/requirements.rb bin/annotate
lib/annotate_models.rb lib/annotate.rb
lib/annotate_models/version.rb lib/annotate/annotate_models.rb
lib/annotate_models/tasks.rb lib/annotate/annotate_routes.rb
lib/tasks/annotate.rake lib/tasks/annotate_models.rake
log/debug.log lib/tasks/annotate_routes.rake
script/console
script/destroy
script/generate
setup.rb
spec/annotate/annotate_models_spec.rb
spec/annotate/annotate_routes_spec.rb
spec/annotate_spec.rb
spec/spec.opts
spec/spec_helper.rb
tasks/rspec.rake
History.txt
License.txt
Manifest.txt
README.rdoc
Rakefile
annotate.gemspec
bin/annotate
lib/annotate.rb
lib/annotate/annotate_models.rb
lib/annotate/annotate_routes.rb
lib/tasks/annotate_models.rake
lib/tasks/annotate_routes.rake
script/console
script/destroy script/destroy
script/generate script/generate
script/txt2html setup.rb
spec/annotate/annotate_models_spec.rb
spec/annotate/annotate_routes_spec.rb
spec/annotate_spec.rb
spec/spec.opts
spec/spec_helper.rb
tasks/rspec.rake
History.txt
License.txt
Manifest.txt
README.rdoc
Rakefile
annotate.gemspec
bin/annotate bin/annotate
lib/annotate.rb
lib/annotate/annotate_models.rb
lib/annotate/annotate_routes.rb
lib/tasks/annotate_models.rake
lib/tasks/annotate_routes.rake
script/console
script/destroy
script/generate
setup.rb setup.rb
tasks/deployment.rake spec/annotate/annotate_models_spec.rb
tasks/environment.rake spec/annotate/annotate_routes_spec.rb
tasks/website.rake spec/annotate_spec.rb
test/test_annotate_models.rb spec/spec.opts
test/test_helper.rb spec/spec_helper.rb
website/index.html tasks/rspec.rake
website/index.txt
website/javascripts/rounded_corners_lite.inc.js
website/stylesheets/screen.css
website/template.rhtml
== AnnotateModels
Add a comment summarizing the current schema to the bottom of each
ActiveRecord model, fixture file.
If you are using Object Daddy, it`ll annotate your example files too.
# == Schema Info
#
# Table name: line_items
#
# id :integer(11) not null, primary key
# quantity :integer(11) not null
# product_id :integer(11) not null
# unit_price :float
# order_id :integer(11)
#
class LineItem < ActiveRecord::Base
belongs_to :product
. . .
Annotates geometrical columns, geom type and srid, when using SpatialAdapter or PostgisAdapter:
# == Schema Info
#
# Table name: trips
#
# local :geometry point, 4326
# path :geometry line_string, 4326
== Warning
Note that this code will blow away the initial/final comment
block in your models if it looks like it was previously added
by annotate models, so you don't want to add additional text
to an automatically created comment block.
* * Back up your model files before using... * *
== Install
sudo gem install nofxx-annotate
== Usage
To annotate all your models:
cd /path/to/app
annotate
To annotate routes.rb:
annotate -r
More:
annotate -h
Add annotation at the beginning or end of the file:
annotate -p [before|after]
Remove annotations:
annotate -d
== LICENSE:
Released under the same license as Ruby. No Support. No Warranty.
== Author:
Original code by:
Dave Thomas -- Pragmatic Programmers, LLC
Modifications by:
- alex@pivotallabs.com
- Cuong Tran - http://github.com/ctran
- Jack Danger - http://github.com/JackDanger
- Michael Bumann - http://github.com/bumi
- Henrik Nyh - http://github.com/henrik
- Marcos Piccinini - http://github.com/nofxx
and many others that I may have missed to add.
require 'config/requirements' %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
require 'config/hoe' # setup Hoe + all gem configuration require 'lib/annotate'
Dir['tasks/**/*.rake'].each { |rake| load rake } # Generate all the Rake tasks
\ No newline at end of file # Run 'rake -T' to see list of generated tasks (from gem root directory)
$hoe = Hoe.new('annotate', Annotate::VERSION) do |p|
p.developer('Cuong Tran', 'ctran@pragmaquest.com')
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
p.rubyforge_name = p.name
p.url = "http://github.com/ctran/annotate_models"
p.summary = "Annotates Rails Models, routes, and others"
p.description = "Annotates Rails Models, routes, and others"
p.extra_dev_deps = [
['newgem', ">= #{::Newgem::VERSION}"]
]
p.clean_globs |= %w[**/.DS_Store tmp *.log]
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
p.rsync_args = '-av --delete --ignore-errors'
end
require 'newgem/tasks'
Dir['tasks/**/*.rake'].each { |t| load t }
# TODO - want other tests/tasks run by default? Add them to the list
# task :default => [:spec, :features]
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{annotate}
s.version = "2.0.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Cuong Tran"]
s.date = %q{2009-02-03}
s.default_executable = %q{annotate}
s.description = %q{Annotates Rails Models, routes, and others}
s.email = ["ctran@pragmaquest.com"]
s.executables = ["annotate"]
s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "README.rdoc"]
s.files = ["History.txt", "License.txt", "Manifest.txt", "README.rdoc", "Rakefile", "annotate.gemspec", "bin/annotate", "lib/annotate.rb", "lib/annotate/annotate_models.rb", "lib/annotate/annotate_routes.rb", "lib/tasks/annotate_models.rake", "lib/tasks/annotate_routes.rake", "script/console", "script/destroy", "script/generate", "setup.rb", "spec/annotate/annotate_models_spec.rb", "spec/annotate/annotate_routes_spec.rb", "spec/annotate_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake"]
s.has_rdoc = true
s.homepage = %q{http://github.com/ctran/annotate_models}
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{annotate}
s.rubygems_version = %q{1.3.1}
s.summary = %q{Annotates Rails Models, routes, and others}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 2
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<newgem>, [">= 1.2.3"])
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
else
s.add_dependency(%q<newgem>, [">= 1.2.3"])
s.add_dependency(%q<hoe>, [">= 1.8.0"])
end
else
s.add_dependency(%q<newgem>, [">= 1.2.3"])
s.add_dependency(%q<hoe>, [">= 1.8.0"])
end
end
#!/usr/bin/env ruby #!/usr/bin/env ruby
require 'optparse' require 'optparse'
require 'annotate_models/tasks' require 'annotate'
task = "annotate_models" task = :annotate_models
OptionParser.new do |opts| OptionParser.new do |opts|
opts.banner = "Usage: annotate [options]" opts.banner = "Usage: annotate [options]"
opts.on('-d', '--delete') { task = "remove_annotation" } opts.on('-d', '--delete') { task = :remove_annotation }
opts.on('-p', '--position [before|after]', ['before', 'after']) { |p| ENV['position'] = p } opts.on('-p', '--position [before|after]', ['before', 'after']) { |p| ENV['position'] = p }
opts.on('-r', '--routes') { task = :annotate_routes }
opts.on('-v', '--version') { puts "Annotate v#{Annotate::VERSION}"; exit }
end.parse! end.parse!
Rake::Task[task].invoke begin
Rake::Task[task].invoke
#TODO: rescue only rake error
rescue NameError => e
puts "Can`t find Rake. Are we in a Rails folder?"
end
unless defined?(Annotate)
$:.unshift(File.dirname(__FILE__))
module Annotate
VERSION = '2.0.0'
end
begin
load 'Rakefile'
Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
rescue LoadError => e
nil
end
end
\ No newline at end of file
module AnnotateModels
class << self
# Annotate Models plugin use this header
COMPAT_PREFIX = "== Schema Info"
PREFIX = "== Schema Information"
MODEL_DIR = "app/models"
FIXTURE_DIRS = ["test/fixtures","spec/fixtures"]
# File.join for windows reverse bar compat?
# I dont use windows, can`t test
UNIT_TEST_DIR = File.join("test", "unit" )
SPEC_MODEL_DIR = File.join("spec", "models")
# Object Daddy http://github.com/flogic/object_daddy/tree/master
EXEMPLARS_DIR = File.join("spec", "exemplars")
# 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
# 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
# Check out if we got a geometric column
# and print the type and SRID
if col.respond_to?(:geometry_type)
attrs << "#{col.geometry_type}, #{col.srid}"
end
info << sprintf("# %-#{max_size}.#{max_size}s:%-15.15s %s", col.name, col_type, attrs.join(", ")).rstrip + "\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 Information"), check if it
# matches the block that is already there. If so, leave it be. If not, remove the old
# info block and write a new one.
# Returns true or false depending on whether the file was modified.
#
# === Options (opts)
# :position<Symbol>:: where to place the annotated section in fixture or model file,
# "before" or "after". Default is "before".
# :position_in_class<Symbol>:: where to place the annotated section in model file
# :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
#
def annotate_one_file(file_name, info_block, options={})
if File.exist?(file_name)
old_content = File.read(file_name)
# Ignore the Schema version line because it changes with each migration
header = Regexp.new(/(^# Table name:.*?\n(#.*\n)*\n)/)
old_header = old_content.match(header).to_s
new_header = info_block.match(header).to_s
if old_header == new_header
false
else
# Remove old schema info
old_content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
# Write it back
new_content = (options[:position].to_sym == :before) ? (info_block + old_content) : (old_content + "\n" + info_block)
File.open(file_name, "wb") { |f| f.puts new_content }
true
end
end
end
def remove_annotation_of_file(file_name)
if File.exist?(file_name)
content = File.read(file_name)
content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
File.open(file_name, "wb") { |f| f.puts 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.
# Returns true or false depending on whether the source
# files were modified.
def annotate(klass, file, header,options={})
info = get_schema_info(klass, header)
annotated = false
model_name = klass.name.underscore
model_file_name = File.join(MODEL_DIR, file)
if annotate_one_file(model_file_name, info, options.merge(
:position=>(options[:position_in_class] || options[:position])))
annotated = true
end
[
File.join(UNIT_TEST_DIR, "#{model_name}_test.rb"), # test
File.join(SPEC_MODEL_DIR, "#{model_name}_spec.rb"), # spec
File.join(EXEMPLARS_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
].each { |file| annotate_one_file(file, info) }
FIXTURE_DIRS.each do |dir|
fixture_file_name = File.join(dir,klass.table_name + ".yml")
annotate_one_file(fixture_file_name, info, options.merge(:position=>(options[:position_in_fixture] || options[:position]))) if File.exist?(fixture_file_name)
end
annotated
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 get_model_files
models = ARGV.dup
models.shift
models.reject!{|m| m.starts_with?("position=")}
if models.empty?
Dir.chdir(MODEL_DIR) do
models = Dir["**/*.rb"]
end
end
models
end
# 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)
file.gsub(/\.rb$/, '').camelize.constantize
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 do_annotations(options={})
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?
if annotate(klass, file, header,options)
annotated << klass
end
end
rescue Exception => e
puts "Unable to annotate #{file}: #{e.message}"
end
end
if annotated.empty?
puts "Nothing annotated!"
else
puts "Annotated (#{annotated.length}): #{annotated.join(', ')}"
end
end
def remove_annotations
deannotated = []
get_model_files.each do |file|
begin
klass = get_model_class(file)
if klass < ActiveRecord::Base && !klass.abstract_class?
deannotated << klass
model_file_name = File.join(MODEL_DIR, file)
remove_annotation_of_file(model_file_name)
FIXTURE_DIRS.each do |dir|
fixture_file_name = File.join(dir,klass.table_name + ".yml")
remove_annotation_of_file(fixture_file_name) if File.exist?(fixture_file_name)
end
end
rescue Exception => e
puts "Unable to annotate #{file}: #{e.message}"
end
end
puts "Removed annotation from: #{deannotated.join(', ')}"
end
end
end
# == Annotate Routes
#
# Based on:
#
#
#
# Prepends the output of "rake routes" to the top of your routes.rb file.
# Yes, it's simple but I'm thick and often need a reminder of what my routes mean.
#
# Running this task will replace any exising route comment generated by the task.
# Best to back up your routes file before running:
#
# Author:
# Gavin Montague
# gavin@leftbrained.co.uk
#
# Released under the same license as Ruby. No Support. No Warranty.module AnnotateRoutes
#
module AnnotateRoutes
PREFIX = "#== Route Map"
def self.do_annotate
routes_rb = File.join("config", "routes.rb")
header = PREFIX + "\n# Generated on #{Time.now.strftime("%d %b %Y %H:%M")}\n#"
if File.exists? routes_rb
routes_map = `rake routes`
routes_map = routes_map.split("\n")
routes_map.shift # remove the first line of rake routes which is just a file path
routes_map = routes_map.inject(header){|sum, line| sum<<"\n# "<<line}
content = File.read(routes_rb)
content, old = content.split(/^#== Route .*?\n/)
File.open(routes_rb, "wb") do |f|
f.puts content.sub!(/\n?\z/, "\n") + routes_map
end
puts "Route file annotated."
else
puts "Can`t find routes.rb"
end
end
end
desc "Add schema information (as comments) to model and fixture files"
task :annotate_models => :environment do
require 'annotate/annotate_models'
options={}
options[:position_in_class] = ENV['position_in_class'] || ENV['position'] || :before
options[:position_in_fixture] = ENV['position_in_fixture'] || ENV['position'] || :before
AnnotateModels.do_annotations(options)
end
desc "Remove schema information from model and fixture files"
task :remove_annotation => :environment do
require 'annotate/annotate_models'
AnnotateModels.remove_annotations
end
desc "Prepends the route map to the top of routes.rb"
task :annotate_routes do
require 'annotate/annotate_routes'
AnnotateRoutes.do_annotate
end
#!/usr/bin/env ruby
# File: script/console
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
libs = " -r irb/completion"
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
libs << " -r #{File.dirname(__FILE__) + '/../lib/annotate.rb'}"
puts "Loading annotate gem"
exec "#{irb} #{libs} --simple-prompt"
\ No newline at end of file
require File.dirname(__FILE__) + '/../spec_helper.rb'
require 'annotate/annotate_models'
describe AnnotateModels do
def mock_klass(stubs={})
@mock_file ||= mock("Klass", stubs)
end
def mock_column(stubs={})
@mock_column ||= mock("Column", stubs)
end
it { AnnotateModels.quote(nil).should eql("NULL") }
it { AnnotateModels.quote(true).should eql("TRUE") }
it { AnnotateModels.quote(false).should eql("FALSE") }
it { AnnotateModels.quote(25).should eql("25") }
it { AnnotateModels.quote(25.6).should eql("25.6") }
it { AnnotateModels.quote(1e-20).should eql("1.0e-20") }
it "should get schema info" do
AnnotateModels.get_schema_info(mock_klass(
:table_name => "users",
:primary_key => "id",
:column_names => ["id","login"],
:columns => [
mock_column(:type => "integer", :default => nil, :null => false, :name => "id", :limit => nil),
mock_column(:type => "string", :default => nil, :null => false, :name => "name", :limit => 50)
]), "Schema Info").should eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# id :integer not null, primary key
#
EOS
end
end
require File.dirname(__FILE__) + '/../spec_helper.rb'
require 'annotate/annotate_routes'
describe AnnotateRoutes do
def mock_file(stubs={})
@mock_file ||= mock(File, stubs)
end
describe "Annotate Job" do
before(:each) do
File.should_receive(:join).with("config", "routes.rb").and_return("config/routes.rb")
end
it "should check if routes.rb exists" do
File.should_receive(:exists?).with("config/routes.rb").and_return(false)
AnnotateRoutes.should_receive(:puts).with("Can`t find routes.rb")
AnnotateRoutes.do_annotate
end
describe "When Annotating" do
before(:each) do
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
AnnotateRoutes.should_receive(:`).with("rake routes").and_return("bad line\ngood line")
File.should_receive(:open).with("config/routes.rb", "wb").and_yield(mock_file)
AnnotateRoutes.should_receive(:puts).with("Route file annotated.")
end
it "should annotate and add a newline!" do
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo")
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n#== Route Map\n# Generated on .*\n#\n# good line/)
AnnotateRoutes.do_annotate
end
it "should not add a newline if there are empty lines" do
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo\n")
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n#== Route Map\n# Generated on .*\n#\n# good line/)
AnnotateRoutes.do_annotate
end
end
end
end
require File.dirname(__FILE__) + '/spec_helper.rb'
describe Annotate do
it "should have a version" do
Annotate::VERSION.should be_instance_of(String)
end
end
--colour
\ No newline at end of file
begin
require 'spec'
rescue LoadError
require 'rubygems'
gem 'rspec'
require 'spec'
end
require File.dirname(__FILE__) + '/../lib/annotate'
begin
require 'spec'
rescue LoadError
require 'rubygems'
require 'spec'
end
begin
require 'spec/rake/spectask'
rescue LoadError
puts <<-EOS
To use rspec for testing you must install rspec gem:
gem install rspec
EOS
exit(0)
end
desc "Run the specs under spec/models"
Spec::Rake::SpecTask.new do |t|
t.spec_opts = ['--options', "spec/spec.opts"]
t.spec_files = FileList['spec/**/*_spec.rb']
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment