annotate 3.0.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|