Skip to content

Commit 69cd69c

Browse files
committed
Add configurable for routes position, ability to remove routes, fix handling of output from newer Rake versions.
1 parent 22b97fa commit 69cd69c

File tree

6 files changed

+216
-42
lines changed

6 files changed

+216
-42
lines changed

CHANGELOG.rdoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
* Bugfix: Load the Rakefile from the current directory, not the first Rakefile
77
in our load path.
88
* Added support for new FactoryGirl naming convention.
9+
* Fix behavior of route annotations in newer versions of Rake that don't spit
10+
out the CWD as their first line of output.
11+
* Routes can now be appended, pre-pended, or removed -- and do sane things in
12+
all cases.
913
* Expose all `position_*` variables as CLI params.
1014
* Make `ENV ['position']` work as a default for all the `ENV ['position_*']`
1115
variables.

README.rdoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ anywhere in the file:
112112
Place the annotations at the top (before) or the bottom (after) of any fixture files
113113
--pt, --position-in-test [before|after]
114114
Place the annotations at the top (before) or the bottom (after) of any test files
115+
--pr, --position-in-routes [before|after]
116+
Place the annotations at the top (before) or the bottom (after) of the routes.rb file
115117
-r, --routes Annotate routes.rb with the output of 'rake routes'
116118
-v, --version Show the current version of this gem
117119
-m, --show-migration Include the migration version number in the annotation

bin/annotate

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@ OptionParser.new do |opts|
2525
opts.banner = "Usage: annotate [options] [model_file]*"
2626

2727
opts.on('-d', '--delete',
28-
"Remove annotations from all model files") do
29-
task = :remove_annotation
28+
"Remove annotations from all model files or the routes.rb file") do
29+
if(task == :annotate_routes)
30+
task = :remove_routes
31+
else
32+
task = :remove_annotation
33+
end
3034
end
3135

3236
ENV['position'] = 'before' # hack: make sure default position is "before"
3337
opts.on('-p', '--position [before|after]', ['before', 'after'],
3438
"Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory file(s)") do |p|
3539
ENV['position'] = p
3640
[
37-
'position_in_class','position_in_factory','position_in_fixture','position_in_test'
41+
'position_in_class','position_in_factory','position_in_fixture','position_in_test', 'position_in_routes'
3842
].each do |key|
3943
ENV[key] = p unless(has_set_position[key])
4044
end
@@ -64,6 +68,12 @@ OptionParser.new do |opts|
6468
has_set_position['position_in_test'] = true
6569
end
6670

71+
opts.on('--pr', '--position-in-routes [before|after]', ['before', 'after'],
72+
"Place the annotations at the top (before) or the bottom (after) of the routes.rb file") do |p|
73+
ENV['position_in_test'] = p
74+
has_set_position['position_in_routes'] = true
75+
end
76+
6777
opts.on('-r', '--routes',
6878
"Annotate routes.rb with the output of 'rake routes'") do
6979
task = :annotate_routes

lib/annotate/annotate_routes.rb

Lines changed: 121 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,130 @@
1818
# Released under the same license as Ruby. No Support. No Warranty.
1919
#
2020
module AnnotateRoutes
21-
PREFIX = "#== Route Map"
22-
23-
def self.do_annotate
24-
routes_rb = File.join("config", "routes.rb")
25-
header = PREFIX + "\n# Generated on #{Time.now.strftime("%d %b %Y %H:%M")}\n#"
26-
if File.exists? routes_rb
27-
routes_map = `rake routes`
28-
routes_map = routes_map.split("\n")
29-
routes_map.shift # remove the first line of rake routes which is just a file path
30-
routes_map = routes_map.inject(header){|sum, line| sum<<"\n# "<<line}
31-
content = File.read(routes_rb)
32-
content, old = content.split(/^#== Route .*?\n/)
33-
File.open(routes_rb, "wb") do |f|
34-
f.puts content.sub!(/\n?\z/, "\n") + routes_map
21+
PREFIX = "# == Route Map"
22+
23+
def self.do_annotate(options={})
24+
return unless(routes_exists?)
25+
26+
position_after = options[:position_in_routes] != 'before'
27+
28+
routes_map = `rake routes`.split(/\n/, -1)
29+
30+
# In old versions of Rake, the first line of output was the cwd. Not so
31+
# much in newer ones. We ditch that line if it exists, and if not, we
32+
# keep the line around.
33+
routes_map.shift if(routes_map.first =~ /^\(in \//)
34+
35+
header = [
36+
"#{PREFIX} (Updated #{Time.now.strftime("%Y-%m-%d %H:%M")})",
37+
"#"
38+
] + routes_map.map { |line| "# #{line}".rstrip }
39+
40+
(content, where_header_found) = strip_annotations(File.read(routes_file))
41+
changed = where_header_found != 0 # This will either be :before, :after, or
42+
# a number. If the number is > 0, the
43+
# annotation was found somewhere in the
44+
# middle of the file. If the number is
45+
# zero, no annotation was found.
46+
47+
if(position_after)
48+
# Ensure we have adequate trailing newlines at the end of the file to
49+
# ensure a blank line separating the content from the annotation.
50+
content << '' if(content.last != '')
51+
52+
# We're moving something from the top of the file to the bottom, so ditch
53+
# the spacer we put in the first time around.
54+
if(changed && where_header_found == :before)
55+
content.shift if(content.first == '')
3556
end
36-
puts "Route file annotated."
3757
else
38-
puts "Can`t find routes.rb"
58+
header = header << '' if(content.first != '' || changed)
3959
end
60+
61+
content = position_after ? (content + header) : header + content
62+
63+
write_contents(content)
64+
65+
puts "Route file annotated."
66+
end
67+
68+
def self.remove_annotations(options={})
69+
return unless(routes_exists?)
70+
71+
(content, where_header_found) = strip_annotations(File.read(routes_file))
72+
73+
content = strip_on_removal(content, where_header_found)
74+
75+
write_contents(content)
76+
77+
puts "Removed annotations from routes file."
4078
end
4179

80+
protected
81+
82+
def self.routes_file
83+
@routes_rb ||= File.join("config", "routes.rb")
84+
end
85+
86+
def self.routes_exists?
87+
routes_exists = File.exists?(routes_file)
88+
puts "Can`t find routes.rb" if(!routes_exists)
89+
return routes_exists
90+
end
91+
92+
def self.write_contents(content)
93+
content << '' unless(content.last == '') # Make sure we end on a trailing
94+
# newline.
95+
96+
File.open(routes_file, "wb") { |f| f.puts(content.join("\n")) }
97+
end
98+
99+
def self.strip_annotations(content)
100+
real_content = []
101+
mode = :content
102+
line_number = 0
103+
header_found_at = 0
104+
content.split(/\n/, -1).each do |line|
105+
line_number += 1
106+
begin
107+
if(mode == :header)
108+
if(line !~ /\s*#/)
109+
mode = :content
110+
raise unless (line == '')
111+
end
112+
elsif(mode == :content)
113+
if(line =~ /^\s*#\s*== Route.*$/)
114+
header_found_at = line_number
115+
mode = :header
116+
else
117+
real_content << line
118+
end
119+
end
120+
rescue
121+
retry
122+
end
123+
end
124+
content_lines = real_content.count
125+
126+
# By default assume the annotation was found in the middle of the file...
127+
where_header_found = header_found_at
128+
# ... unless we have evidence it was at the beginning ...
129+
where_header_found = :before if(header_found_at == 1)
130+
# ... or that it was at the end.
131+
where_header_found = :after if(header_found_at >= content_lines)
132+
133+
return real_content, where_header_found
134+
end
135+
136+
def self.strip_on_removal(content, where_header_found)
137+
if(where_header_found == :before)
138+
content.shift while(content.first == '')
139+
elsif(where_header_found == :after)
140+
content.pop while(content.last == '')
141+
end
142+
# TODO: If the user buried it in the middle, we should probably see about
143+
# TODO: preserving a single line of space between the content above and
144+
# TODO: below...
145+
return content
146+
end
42147
end

lib/tasks/annotate_routes.rake

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1-
desc "Prepends the route map to the top of routes.rb"
1+
desc "Adds the route map to routes.rb"
22
task :annotate_routes => :environment do
33
annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__)))
44
require "#{annotate_lib}/annotate/annotate_routes"
5-
AnnotateRoutes.do_annotate
5+
6+
options={}
7+
options[:position_in_routes] = ENV['position_in_routes'] || ENV['position'] || 'after'
8+
AnnotateRoutes.do_annotate(options)
9+
end
10+
11+
desc "Removes the route map from routes.rb"
12+
task :remove_routes => :environment do
13+
annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__)))
14+
require "#{annotate_lib}/annotate/annotate_routes"
15+
16+
options={}
17+
AnnotateRoutes.remove_annotations(options)
618
end

spec/annotate/annotate_routes_spec.rb

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,76 @@ def mock_file(stubs={})
77
@mock_file ||= mock(File, stubs)
88
end
99

10-
describe "Annotate Job" do
10+
it "should check if routes.rb exists" do
11+
File.should_receive(:exists?).with("config/routes.rb").and_return(false)
12+
AnnotateRoutes.should_receive(:puts).with("Can`t find routes.rb")
13+
AnnotateRoutes.do_annotate
14+
end
15+
16+
describe "When Annotating, with older Rake Versions" do
17+
18+
before(:each) do
19+
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
20+
AnnotateRoutes.should_receive(:`).with("rake routes").and_return("(in /bad/line)\ngood line")
21+
File.should_receive(:open).with("config/routes.rb", "wb").and_yield(mock_file)
22+
AnnotateRoutes.should_receive(:puts).with("Route file annotated.")
23+
end
1124

12-
it "should check if routes.rb exists" do
13-
File.should_receive(:exists?).with("config/routes.rb").and_return(false)
14-
AnnotateRoutes.should_receive(:puts).with("Can`t find routes.rb")
25+
it "should annotate and add a newline!" do
26+
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo")
27+
@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/)
1528
AnnotateRoutes.do_annotate
1629
end
1730

18-
describe "When Annotating" do
31+
it "should not add a newline if there are empty lines" do
32+
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo\n")
33+
@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/)
34+
AnnotateRoutes.do_annotate
35+
end
1936

20-
before(:each) do
21-
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
22-
AnnotateRoutes.should_receive(:`).with("rake routes").and_return("(in /bad/line)\ngood line")
23-
File.should_receive(:open).with("config/routes.rb", "wb").and_yield(mock_file)
24-
AnnotateRoutes.should_receive(:puts).with("Route file annotated.")
25-
end
37+
end
38+
39+
describe "When Annotating, with newer Rake Versions" do
2640

27-
it "should annotate and add a newline!" do
28-
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo")
29-
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n#== Route Map\n# Generated on .*\n#\n# good line/)
30-
AnnotateRoutes.do_annotate
31-
end
41+
before(:each) do
42+
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
43+
AnnotateRoutes.should_receive(:`).with("rake routes").and_return("another good line\ngood line")
44+
File.should_receive(:open).with("config/routes.rb", "wb").and_yield(mock_file)
45+
AnnotateRoutes.should_receive(:puts).with("Route file annotated.")
46+
end
3247

33-
it "should not add a newline if there are empty lines" do
34-
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo\n")
35-
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n#== Route Map\n# Generated on .*\n#\n# good line/)
36-
AnnotateRoutes.do_annotate
37-
end
48+
it "should annotate and add a newline!" do
49+
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo")
50+
@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/)
51+
AnnotateRoutes.do_annotate
52+
end
53+
54+
it "should not add a newline if there are empty lines" do
55+
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo\n")
56+
@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/)
57+
AnnotateRoutes.do_annotate
58+
end
59+
60+
end
61+
62+
describe "When Removing Annotation" do
63+
64+
before(:each) do
65+
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
66+
File.should_receive(:open).with("config/routes.rb", "wb").and_yield(mock_file)
67+
AnnotateRoutes.should_receive(:puts).with("Removed annotations from routes file.")
68+
end
69+
70+
it "should remove trailing annotation and trim trailing newlines, but leave leading newlines alone" do
71+
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")
72+
@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/)
73+
AnnotateRoutes.remove_annotations
74+
end
3875

76+
it "should remove prepended annotation and trim leading newlines, but leave trailing newlines alone" do
77+
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")
78+
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n/)
79+
AnnotateRoutes.remove_annotations
3980
end
4081

4182
end

0 commit comments

Comments
 (0)