Commit e26663b7 by Alex Chaffee

Merge branch 'ctran'

* ctran: (23 commits) Inserting newline requires double-quoted string Annotation position defaulted to 'before'. Bugfix: annotate_models.rake uses string instead of symbol. include ActiveSupport to finish build #45 include lib specs specify :rubygems source in Gemfile for Travis Gemfile for dependencies for Travis CI rake make the specs pass (one pending) fix column pattern Allow task loading from Rakefile for gems (plugin installation already auto-detects). Add skip_on_db_migrate option as well for people that don't want it. Fix options parsing to convert strings to proper booleans. Change annotate to use options hash instead of ENV. Update README.rdoc to mention Fabrication support Add support for Fabrication fabricators We had stripped the trailing newlines from our Schema Information section. This small fix supports that case. rake task is 'build', not 'gem' match indexes and column options too when comparing annotation headers Leave magic encoding comment intact Fixes issue #14 - RuntimeError: Already memoized Count a model as 'annotated' if any of its tests/fixtures are annotated Implement FactoryGirl (#47) fix gemspec and get rspecs partly working again ... Conflicts: Gemfile Rakefile VERSION.yml annotate.gemspec lib/tasks/annotate_models.rake spec/annotate/annotate_models_spec.rb spec/spec_helper.rb
parents f7b2c75a ff1131da
GEM
remote: http://rubygems.org/
specs:
ParseTree (3.0.8)
RubyInline (>= 3.7.0)
sexp_processor (>= 3.0.0)
RubyInline (3.11.2)
ZenTest (~> 4.3)
ZenTest (4.7.0)
activesupport (3.2.2)
i18n (~> 0.6)
multi_json (~> 1.0)
diff-lcs (1.1.3)
file-tail (1.0.8)
tins (~> 0.3)
i18n (0.6.0)
json (1.6.5)
mg (0.0.8)
rake
multi_json (1.1.0)
predicated (0.2.6)
rake (0.9.2.2)
rdoc (3.12)
json (~> 1.4)
rspec (2.9.0)
rspec-core (~> 2.9.0)
rspec-expectations (~> 2.9.0)
rspec-mocks (~> 2.9.0)
rspec-core (2.9.0)
rspec-expectations (2.9.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.9.0)
ruby2ruby (1.3.1)
ruby_parser (~> 2.0)
sexp_processor (~> 3.0)
ruby_parser (2.0.6)
sexp_processor (~> 3.0)
sexp_processor (3.1.0)
sourcify (0.5.0)
file-tail (>= 1.0.5)
ruby2ruby (>= 1.2.5)
ruby_parser (>= 2.0.5)
sexp_processor (>= 3.0.5)
tins (0.3.12)
wrong (0.6.1)
ParseTree (~> 3.0)
diff-lcs (~> 1.1.2)
file-tail (~> 1.0)
predicated (>= 0.2.3)
ruby2ruby (~> 1.2)
ruby_parser (~> 2.0.4)
sexp_processor (~> 3.0)
sourcify (>= 0.3.0)
PLATFORMS
ruby
DEPENDENCIES
activesupport (>= 2.1.0)
mg
rake
rdoc
rspec
wrong
......@@ -7,6 +7,7 @@ Add a comment summarizing the current schema to the top or bottom of each of you
* Tests and Specs
* Object Daddy exemplars
* Machinist blueprints
* Fabrication fabricators
The schema comment looks like this:
......@@ -38,19 +39,25 @@ Also, if you pass the -r option, it'll annotate routes.rb with the output of "ra
== INSTALL
From rubyforge:
Into Gemfile from Github:
sudo gem install annotate
gem 'annotate', :git => 'git://github.com/ctran/annotate_models.git'
From github:
Into environment gems From rubygems.org:
gem install annotate
Into environment gems from Github checkout:
git clone git://github.com/ctran/annotate_models.git annotate
cd annotate
rake gem
sudo gem install pkg/annotate-*.gem
rake build
gem install pkg/annotate-*.gem
== USAGE
(If you used the Gemfile install, prefix the below commands with `bundle exec`.)
To annotate all your models, tests, fixtures, etc.:
cd /path/to/app
......@@ -70,12 +77,26 @@ To annotate routes.rb:
To automatically annotate after running 'rake db:migrate':
[needs more clarity] unpack the gem into vendor/plugins, or maybe vendor/gems, or maybe just require tasks/migrate.rake.
[*needs more clarity*] unpack the gem into vendor/plugins, or maybe vendor/gems, or maybe just require tasks/migrate.rake.
If you install annotate_models as a plugin, it will automatically
adjust your <tt>rake db:migrate</tt> tasks so that they update the
annotations in your model files for you once the migration is
completed.
completed. To get the same behavior from a gem, add the following to
your Rakefile:
require 'annotate/tasks'
To customize the behavior of annotate when it is running as a Rake
task, use the following (in your Rakefile or wherever):
ENV['position_in_class'] = "before"
ENV['position_in_fixture'] = "before"
ENV['show_indexes'] = "false"
ENV['include_version'] = "false"
ENV['exclude_tests'] = "false"
ENV['exclude_fixtures'] = "false"
ENV['skip_on_db_migrate'] = "false"
Warning: ImageMagick installs a tool called `annotate` too (if you're using MacPorts it's in `/opt/local/bin/annotate`. So if you see Usage: annotate imagein.jpg imageout.jpg then put `/usr/bin` ahead on the path and you'll get ours instead.
......@@ -108,6 +129,8 @@ to an automatically created comment block.
* Factory Girl => http://github.com/thoughtbot/factory_girl (NOT IMPLEMENTED)
* Object Daddy => http://github.com/flogic/object_daddy
* Machinist => http://github.com/notahat/machinist
* Fabrication => http://github.com/paulelliott/fabrication
* SpatialAdapter => http://github.com/pdeffendol/spatial_adapter
* PostgisAdapter => http://github.com/nofxx/postgis_adapter
......@@ -142,5 +165,6 @@ Modifications by:
- Bob Potter - http://github.com/bpot
- Gavin Montague - http://github.com/govan/
- Alexander Semyonov - http://github.com/rotuka/
- Ian Duggan http://github.com/ijcd/
and many others that I may have missed to add.
......@@ -34,10 +34,23 @@ end
task :default => :spec
require 'rdoc/task'
RDoc::Task.new do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "annotate #{Annotate.version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
require "rspec/core/rake_task" # RSpec 2.0
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = ['spec/*_spec.rb', 'spec/**/*_spec.rb']
end
# FIXME not working yet
RSpec::Core::RakeTask.new(:rcov) do |t|
t.pattern = 'spec/**/*_spec.rb'
t.rcov = true
end
# FIXME warns "already initialized constant Task"
# FIXME throws "uninitialized constant RDoc::VISIBILITIES"
# require 'rdoc/task'
# RDoc::Task.new do |rdoc|
# rdoc.main = "README.rdoc"
# rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb")
# # require 'lib/annotate'
# # rdoc.title = "annotate #{Annotate.version}"
# end
......@@ -2,3 +2,5 @@
:major: 2
:minor: 5
:patch: 0
:build: 'beta1'
......@@ -2,13 +2,13 @@ require './lib/annotate'
Gem::Specification.new do |s|
s.name = %q{annotate}
s.version = Annotate.version # "2.5.0"
s.version = Annotate.version
s.description = %q{Annotates Rails/ActiveRecord Models, routes, fixtures, and others based on the database schema.}
s.summary = %q{Annotates Rails Models, routes, fixtures, and others based on the database schema.}
s.authors = ["Cuong Tran", "Alex Chaffee", "Marcos Piccinini"]
s.email = ["alex@stinky.com", "ctran@pragmaquest.com", "x@nofxx.com"]
s.authors = ["Cuong Tran", "Alex Chaffee", "Marcos Piccinini", "Turadg Aleahmad"]
s.email = ["alex@stinky.com", "ctran@pragmaquest.com", "x@nofxx.com", "turadg@aleahmad.net"]
s.executables = ["annotate"] # todo: annotate_models
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.extra_rdoc_files = ["README.rdoc"]
s.files = %w( README.rdoc VERSION.yml History.txt )
......@@ -23,3 +23,4 @@ Gem::Specification.new do |s|
s.add_runtime_dependency 'rake' # ?
end
......@@ -2,13 +2,13 @@ require './lib/annotate'
Gem::Specification.new do |s|
s.name = %q{annotate_models}
s.version = Annotate.version # "2.5.0"
s.version = Annotate.version
s.description = %q{Annotates Rails/ActiveRecord Models, routes, fixtures, and others based on the database schema.}
s.summary = %q{Annotates Rails Models, routes, fixtures, and others based on the database schema.}
s.authors = ["Cuong Tran", "Alex Chaffee", "Marcos Piccinini"]
s.email = ["alex@stinky.com", "ctran@pragmaquest.com", "x@nofxx.com"]
s.authors = ["Cuong Tran", "Alex Chaffee", "Marcos Piccinini", "Turadg Aleahmad"]
s.email = ["alex@stinky.com", "ctran@pragmaquest.com", "x@nofxx.com", "turadg@aleahmad.net"]
s.executables = ["annotate"]# todo: change to annotate_models
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.extra_rdoc_files = ["README.rdoc"]
s.files = %w( README.rdoc VERSION.yml History.txt )
......
......@@ -9,11 +9,18 @@ module AnnotateModels
# 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
# Object Daddy http://github.com/flogic/object_daddy
EXEMPLARS_TEST_DIR = File.join("test", "exemplars")
EXEMPLARS_SPEC_DIR = File.join("spec", "exemplars")
# Machinist http://github.com/notahat/machinist
BLUEPRINTS_DIR = File.join("test", "blueprints")
# FactoryGirl http://github.com/thoughtbot/factory_girl
FACTORIES_TEST_DIR = File.join("test", "factories")
FACTORIES_SPEC_DIR = File.join("spec", "factories")
# Fabrication https://github.com/paulelliott/fabrication.git
FABRICATORS_TEST_DIR = File.join("test", "fabricators")
FABRICATORS_SPEC_DIR = File.join("spec", "fabricators")
def model_dir
@model_dir || "app/models"
......@@ -119,23 +126,28 @@ module AnnotateModels
old_content = File.read(file_name)
# Ignore the Schema version line because it changes with each migration
header = Regexp.new(/(^# Table name:.*?\n(#.*[\r]?\n)*[\r]?\n)/)
old_header = old_content.match(header).to_s
new_header = info_block.match(header).to_s
header_pattern = /(^# Table name:.*?\n(#.*[\r]?\n)*[\r]?\n)/
old_header = old_content.match(header_pattern).to_s
new_header = info_block.match(header_pattern).to_s
column_pattern = /^#[\t ]+\w+[\t ]+.+$/
old_columns = old_header && old_header.scan(column_pattern).sort
new_columns = new_header && new_header.scan(column_pattern).sort
old_columns = old_header && old_header.scan(/#[\t\s]+([\w\d]+)[\t\s]+\:([\d\w]+)/).sort
new_columns = new_header && new_header.scan(/#[\t\s]+([\w\d]+)[\t\s]+\:([\d\w]+)/).sort
encoding = Regexp.new(/(^# encoding:.*\n)|(^# coding:.*\n)|(^# -\*- coding:.*\n)/)
encoding_header = old_content.match(encoding).to_s
if old_columns == new_columns
false
else
# Replace the old schema info with the new schema info
new_content = old_content.sub(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, info_block)
new_content = old_content.sub(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n*/, info_block)
# But, if there *was* no old schema info, we simply need to insert it
if new_content == old_content
new_content = options[:position] == 'before' ?
(info_block + old_content) :
((old_content =~ /\n$/ ? old_content : old_content + '\n') + info_block)
old_content.sub!(encoding, '')
new_content = options[:position] == 'after' ?
(encoding_header + (old_content =~ /\n$/ ? old_content : old_content + "\n") + info_block) :
(encoding_header + info_block + old_content)
end
File.open(file_name, "wb") { |f| f.puts new_content }
......@@ -148,7 +160,7 @@ module AnnotateModels
if File.exist?(file_name)
content = File.read(file_name)
content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n*/, '')
File.open(file_name, "wb") { |f| f.puts content }
end
......@@ -160,7 +172,7 @@ module AnnotateModels
# 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={})
def annotate(klass, file, header, options={})
info = get_schema_info(klass, header, options)
annotated = false
model_name = klass.name.underscore
......@@ -169,37 +181,47 @@ module AnnotateModels
if annotate_one_file(model_file_name, info, options_with_position(options, :position_in_class))
annotated = true
end
unless ENV['exclude_tests']
unless options[:exclude_tests]
[
File.join(UNIT_TEST_DIR, "#{model_name}_test.rb"), # test
File.join(SPEC_MODEL_DIR, "#{model_name}_spec.rb"), # spec
].each do |file|
].each do |file|
# todo: add an option "position_in_test" -- or maybe just ask if anyone ever wants different positions for model vs. test vs. fixture
annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
if annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
annotated = true
end
end
end
unless ENV['exclude_fixtures']
unless options[:exclude_fixtures]
[
File.join(EXEMPLARS_TEST_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
File.join(EXEMPLARS_SPEC_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
File.join(BLUEPRINTS_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
].each do |file|
annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
File.join(FACTORIES_TEST_DIR, "#{model_name.pluralize}.rb"), # FactoryGirl Factories
File.join(FACTORIES_SPEC_DIR, "#{model_name.pluralize}.rb"), # FactoryGirl Factories
File.join(FABRICATORS_TEST_DIR, "#{model_name}_fabricator.rb"), # Fabrication Fabricators
File.join(FABRICATORS_SPEC_DIR, "#{model_name}_fabricator.rb"), # Fabrication Fabricators
].each do |file|
if annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
annotated = true
end
end
FIXTURE_DIRS.each do |dir|
fixture_file_name = File.join(dir,klass.table_name + ".yml")
if File.exist?(fixture_file_name)
annotate_one_file(fixture_file_name, info, options_with_position(options, :position_in_fixture))
if annotate_one_file(fixture_file_name, info, options_with_position(options, :position_in_fixture))
annotated = true
end
end
end
end
annotated
end
# position = :position_in_fixture or :position_in_class
def options_with_position(options, position_in)
options.merge(:position=>(options[position_in] || options[:position]))
......@@ -233,13 +255,20 @@ module AnnotateModels
# Check for namespaced models in subdirectories as well as models
# in subdirectories without namespacing.
def get_model_class(file)
require File.expand_path("#{model_dir}/#{file}") # this is for non-rails projects, which don't get Rails auto-require magic
# this is for non-rails projects, which don't get Rails auto-require magic
require File.expand_path("#{model_dir}/#{file}") unless Module.const_defined?(:Rails)
model = ActiveSupport::Inflector.camelize(file.gsub(/\.rb$/, ''))
parts = model.split('::')
begin
parts.inject(Object) {|klass, part| klass.const_get(part) }
rescue LoadError, NameError
Object.const_get(parts.last)
begin
Object.const_get(parts.last)
rescue LoadError, NameError
Object.const_get(Module.constants.detect{|c|parts.last.downcase == c.downcase})
end
end
end
......@@ -280,7 +309,7 @@ module AnnotateModels
puts "Unable to annotate #{file}: #{e.inspect}"
puts ""
# todo: check if all backtrace lines are in "gems" -- if so, it's an annotate bug, so print the whole stack trace.
# puts e.backtrace.join("\n\t")
# puts e.backtrace.join("\n\t")
end
end
if annotated.empty?
......@@ -309,12 +338,12 @@ module AnnotateModels
fixture_file_name = File.join(dir,klass.table_name + ".yml")
remove_annotation_of_file(fixture_file_name) if File.exist?(fixture_file_name)
end
[ File.join(UNIT_TEST_DIR, "#{klass.name.underscore}_test.rb"),
File.join(SPEC_MODEL_DIR,"#{klass.name.underscore}_spec.rb")].each do |file|
remove_annotation_of_file(file) if File.exist?(file)
end
end
rescue Exception => e
puts "Unable to annotate #{file}: #{e.message}"
......
require 'rubygems'
require 'rake'
# Make tasks visible for Rails also when used as gem.
Dir[File.join(File.dirname(__FILE__), '..', 'tasks', '**/*.rake')].each { |rake| load rake }
Dir[File.join(File.dirname(__FILE__), '..', '..', 'tasks', '**/*.rake')].each { |rake| load rake }
......@@ -3,14 +3,17 @@ annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__)))
desc "Add schema information (as comments) to model and fixture files"
task :annotate_models => :environment do
require "#{annotate_lib}/annotate/annotate_models"
true_re = /(true|t|yes|y|1)$/i
options={}
options[:position_in_class] = ENV['position_in_class'] || ENV['position'] || :before
options[:position_in_fixture] = ENV['position_in_fixture'] || ENV['position'] || :before
options[:show_indexes] = ENV['show_indexes']
options[:simple_indexes] = ENV['simple_indexes']
options[:position_in_class] = ENV['position_in_class'] || ENV['position'] || 'before'
options[:position_in_fixture] = ENV['position_in_fixture'] || ENV['position'] || 'before'
options[:show_indexes] = ENV['show_indexes'] =~ true_re
options[:simple_indexes] = ENV['simple_indexes'] =~ true_re
options[:model_dir] = ENV['model_dir']
options[:include_version] = ENV['include_version']
options[:include_version] = ENV['include_version'] =~ true_re
options[:require] = ENV['require'] ? ENV['require'].split(',') : []
options[:exclude_tests] = ENV['exclude_tests'] =~ true_re
options[:exclude_fixtures] = ENV['exclude_fixtures'] =~ true_re
AnnotateModels.do_annotations(options)
end
......
......@@ -51,6 +51,7 @@ EOS
end
end
# todo: use 'files' gem instead
def create(file, body="hi")
File.open(@dir + '/' + file, "w") do |f|
f.puts(body)
......@@ -76,6 +77,11 @@ EOS
has_many :yah
end
EOS
create('foo_with_capitals.rb', <<-EOS)
class FooWithCAPITALS < ActiveRecord::Base
acts_as_awesome :yah
end
EOS
end
it "should work" do
......@@ -97,6 +103,10 @@ EOS
end.should == ""
end
pending it "should find models with non standard capitalization" do
klass = AnnotateModels.get_model_class("foo_with_capitals.rb")
klass.name.should == "FooWithCAPITALS"
end
end
end
......@@ -11,11 +11,11 @@ namespace :db do
end
namespace :migrate do
[:up, :down, :reset, :redo].each do |t|
[:change, :up, :down, :reset, :redo].each do |t|
task t do
Annotate::Migration.update_annotations
Annotate::Migration.update_annotations
end
end
end
end
end
......@@ -24,9 +24,9 @@ module Annotate
@@working = false
def self.update_annotations
unless @@working
unless @@working || (ENV['skip_on_db_migrate'] =~ /(true|t|yes|y|1)$/i)
@@working = true
Rake::Task['annotate_models'].invoke
Rake::Task['annotate_models'].invoke
end
end
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