Skip to content

Commit

Permalink
Overhaul options handling, and also code loading to allow standalone …
Browse files Browse the repository at this point in the history
…usage again.
  • Loading branch information
MrJoy committed Aug 27, 2012
1 parent 8e07084 commit ebc1b03
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 88 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.rdoc
@@ -1,5 +1,7 @@
== 2.5.0

* It's now possible to use Annotate in standalone ActiveRecord (non-Rails)
projects again.
* Adding note that Markdown is actually MultiMarkdown, and recommending the use
of the `kramdown` engine for parsing it.
* Improved Markdown formatting considerably.
Expand Down Expand Up @@ -49,7 +51,8 @@
* 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
* Fix options parsing to convert strings to proper booleans. Change annotate to
use options hash instead of ENV.
* Add support for Fabrication fabricators
* Leave magic encoding comment intact
* Fix issue #14 - RuntimeError: Already memoized
Expand Down
27 changes: 25 additions & 2 deletions README.rdoc
Expand Up @@ -10,7 +10,7 @@ your...
- Machinist blueprints
- Fabrication fabricators
- Thoughtbot's factory_girl factories, i.e. the (spec|test)/factories/<model>_factory.rb files
- routes.rb file
- routes.rb file (for Rails projects)

The schema comment looks like this:

Expand Down Expand Up @@ -69,6 +69,8 @@ Into environment gems from Github checkout:

(If you used the Gemfile install, prefix the below commands with `bundle exec`.)

=== Usage in Rails

To annotate all your models, tests, fixtures, and factories:

cd /path/to/app
Expand All @@ -95,6 +97,18 @@ To remove routes.rb annotations:
annotate --routes --delete


== Configuration


=== Usage Outside of Rails

Everything above applies, except that --routes is not meaningful, and you will
probably need to explicitly set one or more `--require` option(s), and/or one
or more `--model-dir` options to inform annotate about the structure of your
project and help it bootstrap and load the relevant code.



== Configuration

If you want to always skip annotations on a particular model, add this string
Expand All @@ -111,6 +125,15 @@ Edit this file to control things like output format, where annotations are
added (top or bottom of file), and in which artifacts.


=== Configuration in Rails

To generate a configuration file (in the form of a `.rake` file), to set
default options:

rails g annotate:install

Edit this file to control things like output format, where annotations are
added (top or bottom of file), and in which artifacts.
== Rails Integration

By default, once you've generated a configuration file, annotate will be
Expand All @@ -135,7 +158,7 @@ you can do so with a simple environment variable, instead of editing the

Usage: annotate [options] [model_file]*
-d, --delete Remove annotations from all model files or the routes.rb file
-p, --position [before|after] Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory file(s)
-p, --position [before|after] Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/routes file(s)
--pc, --position-in-class [before|after]
Place the annotations at the top (before) or the bottom (after) of the model file
--pf, --position-in-factory [before|after]
Expand Down
37 changes: 16 additions & 21 deletions bin/annotate
Expand Up @@ -10,31 +10,25 @@ here = File.expand_path(File.dirname __FILE__)
$:<< "#{here}/../lib"

require 'optparse'
begin
require 'rake/dsl_definition'
rescue Exception => e
# We might just be on an old version of Rake...
end
require 'rake'
require 'annotate'
Annotate.bootstrap_rake

task = :annotate_models

target = {
:klass => AnnotateModels,
:task => :do_annotations,
}
has_set_position = {}
OptionParser.new do |opts|
opts.banner = "Usage: annotate [options] [model_file]*"

opts.on('-d', '--delete',
"Remove annotations from all model files or the routes.rb file") do
if(task == :annotate_routes)
task = :remove_routes
else
task = :remove_annotation
end

target[:task] = :remove_annotations
end

opts.on('-p', '--position [before|after]', ['before', 'after'],
"Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory file(s)") do |p|
"Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/routes file(s)") do |p|
ENV['position'] = p
[
'position_in_class','position_in_factory','position_in_fixture','position_in_test', 'position_in_routes'
Expand Down Expand Up @@ -75,7 +69,10 @@ OptionParser.new do |opts|

opts.on('-r', '--routes',
"Annotate routes.rb with the output of 'rake routes'") do
task = :annotate_routes
target = {
:klass => AnnotateRoutes,
:task => :do_annotations
}
end

opts.on('-v', '--version',
Expand Down Expand Up @@ -139,9 +136,7 @@ OptionParser.new do |opts|
end
end.parse!

ENV['is_cli'] = '1'
if Annotate.load_tasks
Rake::Task[task].invoke
else
STDERR.puts "Can't find Rakefile. Are we in a Rails folder?"
end

options=Annotate.setup_options({ :is_rake => !ENV['is_rake'].blank? })
Annotate.eager_load(options)
target[:klass].send(target[:task], options)
138 changes: 126 additions & 12 deletions lib/annotate.rb
@@ -1,25 +1,143 @@
$:.unshift(File.dirname(__FILE__))
require 'annotate/version'
require 'annotate/annotate_models'
require 'annotate/annotate_routes'

begin
# ActiveSupport 3.x...
require 'active_support/hash_with_indifferent_access'
rescue Exception => e
# ActiveSupport 2.x...
require 'active_support/core_ext/hash/indifferent_access'
end

module Annotate
##
# The set of available options to customize the behavior of Annotate.
#
POSITION_OPTIONS=[
:position_in_routes, :position_in_class, :position_in_test,
:position_in_fixture, :position_in_factory, :position,
]
FLAG_OPTIONS=[
:show_indexes, :simple_indexes, :include_version, :exclude_tests,
:exclude_fixtures, :exclude_factories, :ignore_model_sub_dir,
:format_bare, :format_rdoc, :format_markdown, :sort, :force, :trace,
]
OTHER_OPTIONS=[
:model_dir,
]
PATH_OPTIONS=[
:require,
]


##
# Set default values that can be overridden via environment variables.
#
def self.set_defaults(options = {})
return if(@has_set_defaults)
@has_set_defaults = true
options = HashWithIndifferentAccess.new(options)
[POSITION_OPTIONS, FLAG_OPTIONS, PATH_OPTIONS].flatten.each do |key|
if(options.has_key?(key))
default_value = if(options[key].is_a?(Array))
options[key].join(",")
else
options[key]
end
end
default_value = ENV[key.to_s] if(!ENV[key.to_s].blank?)
ENV[key.to_s] = default_value.to_s
end
end

TRUE_RE = /^(true|t|yes|y|1)$/i
def self.setup_options(options = {})
POSITION_OPTIONS.each do |key|
options[key] = fallback(ENV[key.to_s], ENV['position'], 'before')
end
FLAG_OPTIONS.each do |key|
options[key] = true?(ENV[key.to_s])
end
OTHER_OPTIONS.each do |key|
options[key] = (!ENV[key.to_s].blank?) ? ENV[key.to_s] : nil
end
PATH_OPTIONS.each do |key|
options[key] = (!ENV[key.to_s].blank?) ? ENV[key.to_s].split(',') : []
end

if(!options[:model_dir])
options[:model_dir] = 'app/models'
end

return options
end

def self.skip_on_migration?
ENV['skip_on_db_migrate'] =~ TRUE_RE
end

def self.loaded_tasks=(val); @loaded_tasks = val; end
def self.loaded_tasks; return @loaded_tasks; end

def self.load_tasks
if File.exists?('Rakefile')
return if(self.loaded_tasks)
self.loaded_tasks = true
return if(self.loaded_tasks)
self.loaded_tasks = true

require 'rake'
load './Rakefile'
Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
end

Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
return true
def self.load_requires(options)
options[:require].each { |path| require path } if options[:require].count > 0
end

def self.eager_load(options)
self.load_requires(options)
require "annotate/active_record_patch"

if(defined?(Rails))
if(Rails.version.split('.').first.to_i < 3)
Rails.configuration.eager_load_paths.each do |load_path|
matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
require_dependency file.sub(matcher, '\1')
end
end
else
klass = Rails::Application.send(:subclasses).first
klass.eager_load!
end
else
return false
FileList["#{options[:model_dir]}/**/*.rb"].each do |fname|
require File.expand_path(fname)
end
end
end

def self.bootstrap_rake
begin
require 'rake/dsl_definition'
rescue Exception => e
# We might just be on an old version of Rake...
end
require 'rake'

if File.exists?('./Rakefile')
load './Rakefile'
end
Rake::Task[:environment].invoke rescue nil
if(!defined?(Rails))
# Not in a Rails project, so time to load up the parts of
# ActiveSupport we need.
require 'active_support'
require 'active_support/core_ext/class/subclasses'
require 'active_support/core_ext/string/inflections'
end
self.load_tasks
Rake::Task[:set_annotation_options].invoke
end

def self.fallback(*args)
return args.detect { |arg| !arg.blank? }
end
Expand All @@ -29,8 +147,4 @@ def self.true?(val)
return false unless(val =~ TRUE_RE)
return true
end

private

TRUE_RE = /^(true|t|yes|y|1)$/i
end
21 changes: 4 additions & 17 deletions lib/annotate/annotate_models.rb
Expand Up @@ -198,7 +198,7 @@ def get_index_info(klass, options={})
# :position_in_*<Symbol>:: where to place the annotated section in fixture or model file,
# :before or :after. Default is :before.
#
def annotate_one_file(file_name, info_block, options={})
def annotate_one_file(file_name, info_block, position, options={})
if File.exist?(file_name)
old_content = File.read(file_name)
return false if(old_content =~ /# -\*- SkipSchemaAnnotations.*\n/)
Expand Down Expand Up @@ -236,7 +236,7 @@ def annotate_one_file(file_name, info_block, options={})
old_content.sub!(encoding, '')
old_content.sub!(PATTERN, '')

new_content = (options[:position] || 'before').to_s == 'after' ?
new_content = options[position].to_s == 'after' ?
(encoding_header + (old_content.rstrip + "\n\n" + info_block)) :
(encoding_header + info_block + old_content)

Expand Down Expand Up @@ -383,12 +383,6 @@ def get_loaded_model(model_path)
# if its a subclass of ActiveRecord::Base,
# then pass it to the associated block
def do_annotations(options={})
if options[:require]
options[:require].each do |path|
require path
end
end

header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup

if options[:include_version]
Expand Down Expand Up @@ -426,11 +420,6 @@ def annotate_model_file(annotated, file, header, options)
end

def remove_annotations(options={})
if options[:require]
options[:require].each do |path|
require path
end
end

self.model_dir = options[:model_dir] if options[:model_dir]
deannotated = []
Expand All @@ -439,9 +428,8 @@ def remove_annotations(options={})
begin
klass = get_model_class(file)
if klass < ActiveRecord::Base && !klass.abstract_class?
deannotated << klass

model_name = klass.name.underscore
table_name = klass.table_name
model_file_name = File.join(model_dir, file)
deannotated_klass = true if(remove_annotation_of_file(model_file_name))

Expand All @@ -462,9 +450,8 @@ def remove_annotations(options={})
deannotated_klass = true
end
end

deannotated << klass if(deannotated_klass)
end
deannotated << klass if(deannotated_klass)
rescue Exception => e
puts "Unable to deannotate #{file}: #{e.message}"
puts "\t" + e.backtrace.join("\n\t") if options[:trace]
Expand Down
12 changes: 0 additions & 12 deletions lib/annotate/annotate_routes.rb
Expand Up @@ -23,12 +23,6 @@ module AnnotateRoutes
def self.do_annotations(options={})
return unless(routes_exists?)

if options[:require]
options[:require].each do |path|
require path
end
end

position_after = options[:position_in_routes] != 'before'

routes_map = `rake routes`.split(/\n/, -1)
Expand Down Expand Up @@ -74,12 +68,6 @@ def self.do_annotations(options={})
def self.remove_annotations(options={})
return unless(routes_exists?)

if options[:require]
options[:require].each do |path|
require path
end
end

(content, where_header_found) = strip_annotations(File.read(routes_file))

content = strip_on_removal(content, where_header_found)
Expand Down

0 comments on commit ebc1b03

Please sign in to comment.