annotate 3.0.2 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{AUTHORS.rdoc → AUTHORS.md} +2 -2
- data/CHANGELOG.md +326 -0
- data/{README.rdoc → README.md} +139 -105
- data/RELEASE.md +19 -0
- data/annotate.gemspec +11 -28
- data/bin/annotate +3 -3
- data/lib/annotate/annotate_models/file_patterns.rb +127 -0
- data/lib/annotate/annotate_models.rb +156 -181
- data/lib/annotate/annotate_routes/header_generator.rb +113 -0
- data/lib/annotate/annotate_routes/helpers.rb +69 -0
- data/lib/annotate/annotate_routes.rb +44 -177
- data/lib/annotate/constants.rb +33 -0
- data/lib/annotate/helpers.rb +30 -0
- data/lib/annotate/parser.rb +127 -75
- data/lib/annotate/version.rb +1 -1
- data/lib/annotate.rb +21 -80
- data/lib/generators/annotate/templates/auto_annotate_models.rake +3 -1
- data/lib/tasks/annotate_models.rake +36 -35
- data/lib/tasks/annotate_models_migrate.rake +17 -4
- data/lib/tasks/annotate_routes.rake +12 -6
- data/potato.md +41 -0
- metadata +23 -18
- data/CHANGELOG.rdoc +0 -238
- data/TODO.rdoc +0 -11
@@ -1,5 +1,3 @@
|
|
1
|
-
# rubocop:disable Metrics/ModuleLength
|
2
|
-
|
3
1
|
# == Annotate Routes
|
4
2
|
#
|
5
3
|
# Based on:
|
@@ -19,145 +17,73 @@
|
|
19
17
|
#
|
20
18
|
# Released under the same license as Ruby. No Support. No Warranty.
|
21
19
|
#
|
22
|
-
module AnnotateRoutes
|
23
|
-
PREFIX = '== Route Map'.freeze
|
24
|
-
PREFIX_MD = '## Route Map'.freeze
|
25
|
-
HEADER_ROW = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action']
|
26
20
|
|
21
|
+
require_relative './annotate_routes/helpers'
|
22
|
+
require_relative './annotate_routes/header_generator'
|
23
|
+
|
24
|
+
module AnnotateRoutes
|
27
25
|
class << self
|
28
26
|
def do_annotations(options = {})
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
if routes_file_exist?
|
28
|
+
existing_text = File.read(routes_file)
|
29
|
+
content, header_position = Helpers.strip_annotations(existing_text)
|
30
|
+
new_content = annotate_routes(HeaderGenerator.generate(options), content, header_position, options)
|
31
|
+
new_text = new_content.join("\n")
|
32
|
+
|
33
|
+
if rewrite_contents(existing_text, new_text)
|
34
|
+
puts "#{routes_file} was annotated."
|
35
|
+
else
|
36
|
+
puts "#{routes_file} was not changed."
|
37
|
+
end
|
38
|
+
else
|
39
|
+
puts "#{routes_file} could not be found."
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
37
43
|
def remove_annotations(_options={})
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
+
if routes_file_exist?
|
45
|
+
existing_text = File.read(routes_file)
|
46
|
+
content, header_position = Helpers.strip_annotations(existing_text)
|
47
|
+
new_content = strip_on_removal(content, header_position)
|
48
|
+
new_text = new_content.join("\n")
|
49
|
+
if rewrite_contents(existing_text, new_text)
|
50
|
+
puts "Annotations were removed from #{routes_file}."
|
51
|
+
else
|
52
|
+
puts "#{routes_file} was not changed (Annotation did not exist)."
|
53
|
+
end
|
54
|
+
else
|
55
|
+
puts "#{routes_file} could not be found."
|
44
56
|
end
|
45
57
|
end
|
46
58
|
|
47
59
|
private
|
48
60
|
|
49
|
-
def
|
50
|
-
|
51
|
-
puts "Can't find routes.rb" unless routes_exists
|
52
|
-
|
53
|
-
routes_exists
|
61
|
+
def routes_file_exist?
|
62
|
+
File.exist?(routes_file)
|
54
63
|
end
|
55
64
|
|
56
65
|
def routes_file
|
57
66
|
@routes_rb ||= File.join('config', 'routes.rb')
|
58
67
|
end
|
59
68
|
|
60
|
-
def
|
61
|
-
|
62
|
-
new_content = annotate_routes(header, content, where_header_found, options)
|
63
|
-
|
64
|
-
# Make sure we end on a trailing newline.
|
65
|
-
new_content << '' unless new_content.last == ''
|
66
|
-
new_text = new_content.join("\n")
|
67
|
-
|
68
|
-
if existing_text == new_text
|
69
|
-
puts "#{routes_file} unchanged."
|
70
|
-
false
|
71
|
-
else
|
72
|
-
File.open(routes_file, 'wb') { |f| f.puts(new_text) }
|
73
|
-
true
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def header(options = {})
|
78
|
-
routes_map = app_routes_map(options)
|
79
|
-
|
80
|
-
magic_comments_map, routes_map = extract_magic_comments_from_array(routes_map)
|
81
|
-
|
82
|
-
out = []
|
83
|
-
|
84
|
-
magic_comments_map.each do |magic_comment|
|
85
|
-
out << magic_comment
|
86
|
-
end
|
87
|
-
out << '' if magic_comments_map.any?
|
88
|
-
|
89
|
-
out += ["# #{options[:wrapper_open]}"] if options[:wrapper_open]
|
90
|
-
|
91
|
-
out += ["# #{options[:format_markdown] ? PREFIX_MD : PREFIX}" + (options[:timestamp] ? " (Updated #{Time.now.strftime('%Y-%m-%d %H:%M')})" : '')]
|
92
|
-
out += ['#']
|
93
|
-
return out if routes_map.size.zero?
|
94
|
-
|
95
|
-
maxs = [HEADER_ROW.map(&:size)] + routes_map[1..-1].map { |line| line.split.map(&:size) }
|
96
|
-
|
97
|
-
if options[:format_markdown]
|
98
|
-
max = maxs.map(&:max).compact.max
|
99
|
-
|
100
|
-
out += ["# #{content(HEADER_ROW, maxs, options)}"]
|
101
|
-
out += ["# #{content(['-' * max, '-' * max, '-' * max, '-' * max], maxs, options)}"]
|
102
|
-
else
|
103
|
-
out += ["# #{content(routes_map[0], maxs, options)}"]
|
104
|
-
end
|
105
|
-
|
106
|
-
out += routes_map[1..-1].map { |line| "# #{content(options[:format_markdown] ? line.split(' ') : line, maxs, options)}" }
|
107
|
-
out += ["# #{options[:wrapper_close]}"] if options[:wrapper_close]
|
108
|
-
|
109
|
-
out
|
110
|
-
end
|
111
|
-
|
112
|
-
# TODO: write the method doc using ruby rdoc formats
|
113
|
-
# where_header_found => This will either be :before, :after, or
|
114
|
-
# a number. If the number is > 0, the
|
115
|
-
# annotation was found somewhere in the
|
116
|
-
# middle of the file. If the number is
|
117
|
-
# zero, no annotation was found.
|
118
|
-
def strip_annotations(content)
|
119
|
-
real_content = []
|
120
|
-
mode = :content
|
121
|
-
header_found_at = 0
|
122
|
-
|
123
|
-
content.split(/\n/, -1).each_with_index do |line, line_number|
|
124
|
-
if mode == :header && line !~ /\s*#/
|
125
|
-
mode = :content
|
126
|
-
real_content << line unless line.blank?
|
127
|
-
elsif mode == :content
|
128
|
-
if line =~ /^\s*#\s*== Route.*$/
|
129
|
-
header_found_at = line_number + 1 # index start's at 0
|
130
|
-
mode = :header
|
131
|
-
else
|
132
|
-
real_content << line
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
where_header_found(real_content, header_found_at)
|
138
|
-
end
|
139
|
-
|
140
|
-
def strip_on_removal(content, where_header_found)
|
141
|
-
if where_header_found == :before
|
69
|
+
def strip_on_removal(content, header_position)
|
70
|
+
if header_position == :before
|
142
71
|
content.shift while content.first == ''
|
143
|
-
elsif
|
72
|
+
elsif header_position == :after
|
144
73
|
content.pop while content.last == ''
|
145
74
|
end
|
146
75
|
|
76
|
+
# Make sure we end on a trailing newline.
|
77
|
+
content << '' unless content.last == ''
|
78
|
+
|
147
79
|
# TODO: If the user buried it in the middle, we should probably see about
|
148
80
|
# TODO: preserving a single line of space between the content above and
|
149
81
|
# TODO: below...
|
150
82
|
content
|
151
83
|
end
|
152
84
|
|
153
|
-
|
154
|
-
def rewrite_contents(existing_text, new_content)
|
155
|
-
# Make sure we end on a trailing newline.
|
156
|
-
new_content << '' unless new_content.last == ''
|
157
|
-
new_text = new_content.join("\n")
|
158
|
-
|
85
|
+
def rewrite_contents(existing_text, new_text)
|
159
86
|
if existing_text == new_text
|
160
|
-
puts "#{routes_file} unchanged."
|
161
87
|
false
|
162
88
|
else
|
163
89
|
File.open(routes_file, 'wb') { |f| f.puts(new_text) }
|
@@ -165,8 +91,8 @@ module AnnotateRoutes
|
|
165
91
|
end
|
166
92
|
end
|
167
93
|
|
168
|
-
def annotate_routes(header, content,
|
169
|
-
magic_comments_map, content = extract_magic_comments_from_array(content)
|
94
|
+
def annotate_routes(header, content, header_position, options = {})
|
95
|
+
magic_comments_map, content = Helpers.extract_magic_comments_from_array(content)
|
170
96
|
if %w(before top).include?(options[:position_in_routes])
|
171
97
|
header = header << '' if content.first != ''
|
172
98
|
magic_comments_map << '' if magic_comments_map.any?
|
@@ -178,74 +104,15 @@ module AnnotateRoutes
|
|
178
104
|
|
179
105
|
# We're moving something from the top of the file to the bottom, so ditch
|
180
106
|
# the spacer we put in the first time around.
|
181
|
-
content.shift if
|
107
|
+
content.shift if header_position == :before && content.first == ''
|
182
108
|
|
183
109
|
new_content = magic_comments_map + content + header
|
184
110
|
end
|
185
111
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
def app_routes_map(options)
|
190
|
-
routes_map = `rake routes`.chomp("\n").split(/\n/, -1)
|
191
|
-
|
192
|
-
# In old versions of Rake, the first line of output was the cwd. Not so
|
193
|
-
# much in newer ones. We ditch that line if it exists, and if not, we
|
194
|
-
# keep the line around.
|
195
|
-
routes_map.shift if routes_map.first =~ /^\(in \//
|
196
|
-
|
197
|
-
# Skip routes which match given regex
|
198
|
-
# Note: it matches the complete line (route_name, path, controller/action)
|
199
|
-
if options[:ignore_routes]
|
200
|
-
routes_map.reject! { |line| line =~ /#{options[:ignore_routes]}/ }
|
201
|
-
end
|
202
|
-
|
203
|
-
routes_map
|
204
|
-
end
|
205
|
-
|
206
|
-
# @param [Array<String>] content
|
207
|
-
# @return [Array<String>] all found magic comments
|
208
|
-
# @return [Array<String>] content without magic comments
|
209
|
-
def extract_magic_comments_from_array(content_array)
|
210
|
-
magic_comments = []
|
211
|
-
new_content = []
|
212
|
-
|
213
|
-
content_array.map do |row|
|
214
|
-
if row =~ magic_comment_matcher
|
215
|
-
magic_comments << row.strip
|
216
|
-
else
|
217
|
-
new_content << row
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
[magic_comments, new_content]
|
222
|
-
end
|
223
|
-
|
224
|
-
def content(line, maxs, options = {})
|
225
|
-
return line.rstrip unless options[:format_markdown]
|
226
|
-
|
227
|
-
line.each_with_index.map do |elem, index|
|
228
|
-
min_length = maxs.map { |arr| arr[index] }.max || 0
|
229
|
-
|
230
|
-
sprintf("%-#{min_length}.#{min_length}s", elem.tr('|', '-'))
|
231
|
-
end.join(' | ')
|
232
|
-
end
|
233
|
-
|
234
|
-
def where_header_found(real_content, header_found_at)
|
235
|
-
# By default assume the annotation was found in the middle of the file
|
236
|
-
|
237
|
-
# ... unless we have evidence it was at the beginning ...
|
238
|
-
return real_content, :before if header_found_at == 1
|
239
|
-
|
240
|
-
# ... or that it was at the end.
|
241
|
-
return real_content, :after if header_found_at >= real_content.count
|
242
|
-
|
243
|
-
# and the default
|
244
|
-
return real_content, header_found_at
|
245
|
-
end
|
112
|
+
# Make sure we end on a trailing newline.
|
113
|
+
new_content << '' unless new_content.last == ''
|
246
114
|
|
247
|
-
|
248
|
-
Regexp.new(/(^#\s*encoding:.*)|(^# coding:.*)|(^# -\*- coding:.*)|(^# -\*- encoding\s?:.*)|(^#\s*frozen_string_literal:.+)|(^# -\*- frozen_string_literal\s*:.+-\*-)/)
|
115
|
+
new_content
|
249
116
|
end
|
250
117
|
end
|
251
118
|
end
|
data/lib/annotate/constants.rb
CHANGED
@@ -1,5 +1,38 @@
|
|
1
1
|
module Annotate
|
2
2
|
module Constants
|
3
3
|
TRUE_RE = /^(true|t|yes|y|1)$/i.freeze
|
4
|
+
|
5
|
+
##
|
6
|
+
# The set of available options to customize the behavior of Annotate.
|
7
|
+
#
|
8
|
+
POSITION_OPTIONS = [
|
9
|
+
:position_in_routes, :position_in_class, :position_in_test,
|
10
|
+
:position_in_fixture, :position_in_factory, :position,
|
11
|
+
:position_in_serializer
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
FLAG_OPTIONS = [
|
15
|
+
:show_indexes, :simple_indexes, :include_version, :exclude_tests,
|
16
|
+
:exclude_fixtures, :exclude_factories, :ignore_model_sub_dir,
|
17
|
+
:format_bare, :format_rdoc, :format_yard, :format_markdown, :sort, :force, :frozen,
|
18
|
+
:trace, :timestamp, :exclude_serializers, :classified_sort,
|
19
|
+
:show_foreign_keys, :show_complete_foreign_keys,
|
20
|
+
:exclude_scaffolds, :exclude_controllers, :exclude_helpers,
|
21
|
+
:exclude_sti_subclasses, :ignore_unknown_models, :with_comment
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
OTHER_OPTIONS = [
|
25
|
+
:additional_file_patterns, :ignore_columns, :skip_on_db_migrate, :wrapper_open, :wrapper_close,
|
26
|
+
:wrapper, :routes, :models, :hide_limit_column_types, :hide_default_column_types,
|
27
|
+
:ignore_routes, :active_admin
|
28
|
+
].freeze
|
29
|
+
|
30
|
+
PATH_OPTIONS = [
|
31
|
+
:require, :model_dir, :root_dir
|
32
|
+
].freeze
|
33
|
+
|
34
|
+
ALL_ANNOTATE_OPTIONS = [
|
35
|
+
POSITION_OPTIONS, FLAG_OPTIONS, OTHER_OPTIONS, PATH_OPTIONS
|
36
|
+
].freeze
|
4
37
|
end
|
5
38
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Annotate
|
2
|
+
# Class for holding helper methods. Done to make lib/annotate.rb less bloated.
|
3
|
+
class Helpers
|
4
|
+
class << self
|
5
|
+
def skip_on_migration?
|
6
|
+
ENV['ANNOTATE_SKIP_ON_DB_MIGRATE'] =~ Constants::TRUE_RE || ENV['skip_on_db_migrate'] =~ Constants::TRUE_RE
|
7
|
+
end
|
8
|
+
|
9
|
+
def include_routes?
|
10
|
+
ENV['routes'] =~ Constants::TRUE_RE
|
11
|
+
end
|
12
|
+
|
13
|
+
def include_models?
|
14
|
+
ENV['models'] =~ Constants::TRUE_RE
|
15
|
+
end
|
16
|
+
|
17
|
+
def true?(val)
|
18
|
+
val.present? && Constants::TRUE_RE.match?(val)
|
19
|
+
end
|
20
|
+
|
21
|
+
def fallback(*args)
|
22
|
+
args.detect(&:present?)
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset_options(options)
|
26
|
+
options.flatten.each { |key| ENV[key.to_s] = nil }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|