Commit 69cd69cb by Jon Frisby

Add configurable for routes position, ability to remove routes, fix handling of…

Add configurable for routes position, ability to remove routes, fix handling of output from newer Rake versions.
parent 22b97fa6
......@@ -6,6 +6,10 @@
* Bugfix: Load the Rakefile from the current directory, not the first Rakefile
in our load path.
* Added support for new FactoryGirl naming convention.
* Fix behavior of route annotations in newer versions of Rake that don't spit
out the CWD as their first line of output.
* Routes can now be appended, pre-pended, or removed -- and do sane things in
all cases.
* Expose all `position_*` variables as CLI params.
* Make `ENV ['position']` work as a default for all the `ENV ['position_*']`
variables.
......
......@@ -112,6 +112,8 @@ anywhere in the file:
Place the annotations at the top (before) or the bottom (after) of any fixture files
--pt, --position-in-test [before|after]
Place the annotations at the top (before) or the bottom (after) of any test files
--pr, --position-in-routes [before|after]
Place the annotations at the top (before) or the bottom (after) of the routes.rb file
-r, --routes Annotate routes.rb with the output of 'rake routes'
-v, --version Show the current version of this gem
-m, --show-migration Include the migration version number in the annotation
......
......@@ -25,16 +25,20 @@ OptionParser.new do |opts|
opts.banner = "Usage: annotate [options] [model_file]*"
opts.on('-d', '--delete',
"Remove annotations from all model files") do
"Remove annotations from all model files or the routes.rb file") do
if(task == :annotate_routes)
task = :remove_routes
else
task = :remove_annotation
end
end
ENV['position'] = 'before' # hack: make sure default position is "before"
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|
ENV['position'] = p
[
'position_in_class','position_in_factory','position_in_fixture','position_in_test'
'position_in_class','position_in_factory','position_in_fixture','position_in_test', 'position_in_routes'
].each do |key|
ENV[key] = p unless(has_set_position[key])
end
......@@ -64,6 +68,12 @@ OptionParser.new do |opts|
has_set_position['position_in_test'] = true
end
opts.on('--pr', '--position-in-routes [before|after]', ['before', 'after'],
"Place the annotations at the top (before) or the bottom (after) of the routes.rb file") do |p|
ENV['position_in_test'] = p
has_set_position['position_in_routes'] = true
end
opts.on('-r', '--routes',
"Annotate routes.rb with the output of 'rake routes'") do
task = :annotate_routes
......
......@@ -18,25 +18,130 @@
# Released under the same license as Ruby. No Support. No Warranty.
#
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
PREFIX = "# == Route Map"
def self.do_annotate(options={})
return unless(routes_exists?)
position_after = options[:position_in_routes] != 'before'
routes_map = `rake routes`.split(/\n/, -1)
# In old versions of Rake, the first line of output was the cwd. Not so
# much in newer ones. We ditch that line if it exists, and if not, we
# keep the line around.
routes_map.shift if(routes_map.first =~ /^\(in \//)
header = [
"#{PREFIX} (Updated #{Time.now.strftime("%Y-%m-%d %H:%M")})",
"#"
] + routes_map.map { |line| "# #{line}".rstrip }
(content, where_header_found) = strip_annotations(File.read(routes_file))
changed = where_header_found != 0 # This will either be :before, :after, or
# a number. If the number is > 0, the
# annotation was found somewhere in the
# middle of the file. If the number is
# zero, no annotation was found.
if(position_after)
# Ensure we have adequate trailing newlines at the end of the file to
# ensure a blank line separating the content from the annotation.
content << '' if(content.last != '')
# We're moving something from the top of the file to the bottom, so ditch
# the spacer we put in the first time around.
if(changed && where_header_found == :before)
content.shift if(content.first == '')
end
else
header = header << '' if(content.first != '' || changed)
end
content = position_after ? (content + header) : header + content
write_contents(content)
puts "Route file annotated."
end
def self.remove_annotations(options={})
return unless(routes_exists?)
(content, where_header_found) = strip_annotations(File.read(routes_file))
content = strip_on_removal(content, where_header_found)
write_contents(content)
puts "Removed annotations from routes file."
end
protected
def self.routes_file
@routes_rb ||= File.join("config", "routes.rb")
end
def self.routes_exists?
routes_exists = File.exists?(routes_file)
puts "Can`t find routes.rb" if(!routes_exists)
return routes_exists
end
def self.write_contents(content)
content << '' unless(content.last == '') # Make sure we end on a trailing
# newline.
File.open(routes_file, "wb") { |f| f.puts(content.join("\n")) }
end
def self.strip_annotations(content)
real_content = []
mode = :content
line_number = 0
header_found_at = 0
content.split(/\n/, -1).each do |line|
line_number += 1
begin
if(mode == :header)
if(line !~ /\s*#/)
mode = :content
raise unless (line == '')
end
elsif(mode == :content)
if(line =~ /^\s*#\s*== Route.*$/)
header_found_at = line_number
mode = :header
else
puts "Can`t find routes.rb"
real_content << line
end
end
rescue
retry
end
end
content_lines = real_content.count
# By default assume the annotation was found in the middle of the file...
where_header_found = header_found_at
# ... unless we have evidence it was at the beginning ...
where_header_found = :before if(header_found_at == 1)
# ... or that it was at the end.
where_header_found = :after if(header_found_at >= content_lines)
return real_content, where_header_found
end
def self.strip_on_removal(content, where_header_found)
if(where_header_found == :before)
content.shift while(content.first == '')
elsif(where_header_found == :after)
content.pop while(content.last == '')
end
# TODO: If the user buried it in the middle, we should probably see about
# TODO: preserving a single line of space between the content above and
# TODO: below...
return content
end
end
desc "Prepends the route map to the top of routes.rb"
desc "Adds the route map to routes.rb"
task :annotate_routes => :environment do
annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__)))
require "#{annotate_lib}/annotate/annotate_routes"
AnnotateRoutes.do_annotate
options={}
options[:position_in_routes] = ENV['position_in_routes'] || ENV['position'] || 'after'
AnnotateRoutes.do_annotate(options)
end
desc "Removes the route map from routes.rb"
task :remove_routes => :environment do
annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__)))
require "#{annotate_lib}/annotate/annotate_routes"
options={}
AnnotateRoutes.remove_annotations(options)
end
......@@ -7,15 +7,13 @@ describe AnnotateRoutes do
@mock_file ||= mock(File, stubs)
end
describe "Annotate Job" do
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
describe "When Annotating, with older Rake Versions" do
before(:each) do
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
......@@ -26,18 +24,61 @@ describe AnnotateRoutes do
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/)
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map \(Updated \d{4}-\d{2}-\d{2} \d{2}:\d{2}\)\n#\n# good line\n/)
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\n# == Route Map \(Updated \d{4}-\d{2}-\d{2} \d{2}:\d{2}\)\n#\n# good line\n/)
AnnotateRoutes.do_annotate
end
end
describe "When Annotating, with newer Rake Versions" do
before(:each) do
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
AnnotateRoutes.should_receive(:`).with("rake routes").and_return("another good 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\n# == Route Map \(Updated \d{4}-\d{2}-\d{2} \d{2}:\d{2}\)\n#\n# another good line\n# good line\n/)
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/)
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map \(Updated \d{4}-\d{2}-\d{2} \d{2}:\d{2}\)\n#\n# another good line\n# good line\n/)
AnnotateRoutes.do_annotate
end
end
describe "When Removing Annotation" do
before(:each) do
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
File.should_receive(:open).with("config/routes.rb", "wb").and_yield(mock_file)
AnnotateRoutes.should_receive(:puts).with("Removed annotations from routes file.")
end
it "should remove trailing annotation and trim trailing newlines, but leave leading newlines alone" do
File.should_receive(:read).with("config/routes.rb").and_return("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n# == Route Map (Updated 2012-08-16 00:00)\n#\n# another good line\n# good line\n")
@mock_file.should_receive(:puts).with(/\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n/)
AnnotateRoutes.remove_annotations
end
it "should remove prepended annotation and trim leading newlines, but leave trailing newlines alone" do
File.should_receive(:read).with("config/routes.rb").and_return("# == Route Map (Updated 2012-08-16 00:00)\n#\n# another good line\n# good line\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n")
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n/)
AnnotateRoutes.remove_annotations
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