Permalink
Browse files

Overhaul options handling, and also code loading to allow standalone …

…usage again.
  • Loading branch information...
MrJoy committed Aug 27, 2012
1 parent 8e07084 commit ebc1b0373ec194fb31df494a5b6ea9c31e2062fe
View
@@ -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.
@@ -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
View
@@ -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:
@@ -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
@@ -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
@@ -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
@@ -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]
View
@@ -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'
@@ -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',
@@ -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)
View
@@ -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
@@ -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
@@ -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/)
@@ -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)
@@ -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]
@@ -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 = []
@@ -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))
@@ -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]
@@ -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)
@@ -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)
Oops, something went wrong.

0 comments on commit ebc1b03

Please sign in to comment.