annotate 2.7.4 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,7 +10,7 @@
10
10
  # Yes, it's simple but I'm thick and often need a reminder of what my routes
11
11
  # mean.
12
12
  #
13
- # Running this task will replace any exising route comment generated by the
13
+ # Running this task will replace any existing route comment generated by the
14
14
  # task. Best to back up your routes file before running:
15
15
  #
16
16
  # Author:
@@ -19,26 +19,62 @@
19
19
  #
20
20
  # Released under the same license as Ruby. No Support. No Warranty.
21
21
  #
22
+
23
+ require_relative './annotate_routes/helpers'
24
+
22
25
  module AnnotateRoutes
23
26
  PREFIX = '== Route Map'.freeze
24
27
  PREFIX_MD = '## Route Map'.freeze
25
- HEADER_ROW = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action']
28
+ HEADER_ROW = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action'].freeze
26
29
 
27
30
  class << self
28
- def content(line, maxs, options = {})
29
- return line.rstrip unless options[:format_markdown]
31
+ def do_annotations(options = {})
32
+ if routes_file_exist?
33
+ existing_text = File.read(routes_file)
34
+ content, header_position = Helpers.strip_annotations(existing_text)
35
+ new_content = annotate_routes(header(options), content, header_position, options)
36
+ new_text = new_content.join("\n")
37
+
38
+ if rewrite_contents(existing_text, new_text)
39
+ puts "#{routes_file} was annotated."
40
+ else
41
+ puts "#{routes_file} was not changed."
42
+ end
43
+ else
44
+ puts "#{routes_file} could not be found."
45
+ end
46
+ end
30
47
 
31
- line.each_with_index.map do |elem, index|
32
- min_length = maxs.map { |arr| arr[index] }.max || 0
48
+ def remove_annotations(_options={})
49
+ if routes_file_exist?
50
+ existing_text = File.read(routes_file)
51
+ content, header_position = Helpers.strip_annotations(existing_text)
52
+ new_content = strip_on_removal(content, header_position)
53
+ new_text = new_content.join("\n")
54
+ if rewrite_contents(existing_text, new_text)
55
+ puts "Annotations were removed from #{routes_file}."
56
+ else
57
+ puts "#{routes_file} was not changed (Annotation did not exist)."
58
+ end
59
+ else
60
+ puts "#{routes_file} could not be found."
61
+ end
62
+ end
33
63
 
34
- sprintf("%-#{min_length}.#{min_length}s", elem.tr('|', '-'))
35
- end.join(' | ')
64
+ private
65
+
66
+ def routes_file_exist?
67
+ File.exist?(routes_file)
68
+ end
69
+
70
+ def routes_file
71
+ @routes_rb ||= File.join('config', 'routes.rb')
36
72
  end
37
73
 
38
74
  def header(options = {})
39
75
  routes_map = app_routes_map(options)
40
76
 
41
- magic_comments_map, routes_map = extract_magic_comments_from_array(routes_map)
77
+ magic_comments_map, routes_map = Helpers.extract_magic_comments_from_array(routes_map)
42
78
 
43
79
  out = []
44
80
 
@@ -47,10 +83,10 @@ module AnnotateRoutes
47
83
  end
48
84
  out << '' if magic_comments_map.any?
49
85
 
50
- out += ["# #{options[:wrapper_open]}"] if options[:wrapper_open]
86
+ out << comment(options[:wrapper_open]) if options[:wrapper_open]
51
87
 
52
- out += ["# #{options[:format_markdown] ? PREFIX_MD : PREFIX}" + (options[:timestamp] ? " (Updated #{Time.now.strftime('%Y-%m-%d %H:%M')})" : '')]
53
- out += ['#']
88
+ out << comment(options[:format_markdown] ? PREFIX_MD : PREFIX) + (options[:timestamp] ? " (Updated #{Time.now.strftime('%Y-%m-%d %H:%M')})" : '')
89
+ out << comment
54
90
  return out if routes_map.size.zero?
55
91
 
56
92
  maxs = [HEADER_ROW.map(&:size)] + routes_map[1..-1].map { |line| line.split.map(&:size) }
@@ -58,192 +94,100 @@ module AnnotateRoutes
58
94
  if options[:format_markdown]
59
95
  max = maxs.map(&:max).compact.max
60
96
 
61
- out += ["# #{content(HEADER_ROW, maxs, options)}"]
62
- out += ["# #{content(['-' * max, '-' * max, '-' * max, '-' * max], maxs, options)}"]
97
+ out << comment(content(HEADER_ROW, maxs, options))
98
+ out << comment(content(['-' * max, '-' * max, '-' * max, '-' * max], maxs, options))
63
99
  else
64
- out += ["# #{content(routes_map[0], maxs, options)}"]
100
+ out << comment(content(routes_map[0], maxs, options))
65
101
  end
66
102
 
67
- out += routes_map[1..-1].map { |line| "# #{content(options[:format_markdown] ? line.split(' ') : line, maxs, options)}" }
68
- out += ["# #{options[:wrapper_close]}"] if options[:wrapper_close]
103
+ out += routes_map[1..-1].map { |line| comment(content(options[:format_markdown] ? line.split(' ') : line, maxs, options)) }
104
+ out << comment(options[:wrapper_close]) if options[:wrapper_close]
69
105
 
70
106
  out
71
107
  end
72
108
 
73
- def do_annotations(options = {})
74
- return unless routes_exists?
75
- existing_text = File.read(routes_file)
76
-
77
- if rewrite_contents_with_header(existing_text, header(options), options)
78
- puts "#{routes_file} annotated."
109
+ def comment(row = '')
110
+ if row == ''
111
+ '#'
112
+ else
113
+ "# #{row}"
79
114
  end
80
115
  end
81
116
 
82
- def remove_annotations(_options={})
83
- return unless routes_exists?
84
- existing_text = File.read(routes_file)
85
- content, where_header_found = strip_annotations(existing_text)
86
- new_content = strip_on_removal(content, where_header_found)
87
- if rewrite_contents(existing_text, new_content)
88
- puts "Removed annotations from #{routes_file}."
117
+ def strip_on_removal(content, header_position)
118
+ if header_position == :before
119
+ content.shift while content.first == ''
120
+ elsif header_position == :after
121
+ content.pop while content.last == ''
89
122
  end
90
- end
91
- end
92
123
 
93
- def self.magic_comment_matcher
94
- Regexp.new(/(^#\s*encoding:.*)|(^# coding:.*)|(^# -\*- coding:.*)|(^# -\*- encoding\s?:.*)|(^#\s*frozen_string_literal:.+)|(^# -\*- frozen_string_literal\s*:.+-\*-)/)
95
- end
124
+ # Make sure we end on a trailing newline.
125
+ content << '' unless content.last == ''
96
126
 
97
- # @param [Array<String>] content
98
- # @return [Array<String>] all found magic comments
99
- # @return [Array<String>] content without magic comments
100
- def self.extract_magic_comments_from_array(content_array)
101
- magic_comments = []
102
- new_content = []
127
+ # TODO: If the user buried it in the middle, we should probably see about
128
+ # TODO: preserving a single line of space between the content above and
129
+ # TODO: below...
130
+ content
131
+ end
103
132
 
104
- content_array.map do |row|
105
- if row =~ magic_comment_matcher
106
- magic_comments << row.strip
133
+ def rewrite_contents(existing_text, new_text)
134
+ if existing_text == new_text
135
+ false
107
136
  else
108
- new_content << row
137
+ File.open(routes_file, 'wb') { |f| f.puts(new_text) }
138
+ true
109
139
  end
110
140
  end
111
141
 
112
- [magic_comments, new_content]
113
- end
114
-
115
- def self.app_routes_map(options)
116
- routes_map = `rake routes`.chomp("\n").split(/\n/, -1)
117
-
118
- # In old versions of Rake, the first line of output was the cwd. Not so
119
- # much in newer ones. We ditch that line if it exists, and if not, we
120
- # keep the line around.
121
- routes_map.shift if routes_map.first =~ /^\(in \//
122
-
123
- # Skip routes which match given regex
124
- # Note: it matches the complete line (route_name, path, controller/action)
125
- if options[:ignore_routes]
126
- routes_map.reject! { |line| line =~ /#{options[:ignore_routes]}/ }
127
- end
128
-
129
- routes_map
130
- end
131
-
132
- def self.routes_file
133
- @routes_rb ||= File.join('config', 'routes.rb')
134
- end
135
-
136
- def self.routes_exists?
137
- routes_exists = File.exists?(routes_file)
138
- puts "Can't find routes.rb" unless routes_exists
139
-
140
- routes_exists
141
- end
142
+ def annotate_routes(header, content, header_position, options = {})
143
+ magic_comments_map, content = Helpers.extract_magic_comments_from_array(content)
144
+ if %w(before top).include?(options[:position_in_routes])
145
+ header = header << '' if content.first != ''
146
+ magic_comments_map << '' if magic_comments_map.any?
147
+ new_content = magic_comments_map + header + content
148
+ else
149
+ # Ensure we have adequate trailing newlines at the end of the file to
150
+ # ensure a blank line separating the content from the annotation.
151
+ content << '' unless content.last == ''
142
152
 
143
- # @param [String, Array<String>]
144
- def self.rewrite_contents(existing_text, new_content)
145
- # Make sure we end on a trailing newline.
146
- new_content << '' unless new_content.last == ''
147
- new_text = new_content.join("\n")
148
-
149
- if existing_text == new_text
150
- puts "#{routes_file} unchanged."
151
- false
152
- else
153
- File.open(routes_file, 'wb') { |f| f.puts(new_text) }
154
- true
155
- end
156
- end
153
+ # We're moving something from the top of the file to the bottom, so ditch
154
+ # the spacer we put in the first time around.
155
+ content.shift if header_position == :before && content.first == ''
157
156
 
158
- def self.rewrite_contents_with_header(existing_text, header, options = {})
159
- content, where_header_found = strip_annotations(existing_text)
160
- new_content = annotate_routes(header, content, where_header_found, options)
157
+ new_content = magic_comments_map + content + header
158
+ end
161
159
 
162
- # Make sure we end on a trailing newline.
163
- new_content << '' unless new_content.last == ''
164
- new_text = new_content.join("\n")
160
+ # Make sure we end on a trailing newline.
161
+ new_content << '' unless new_content.last == ''
165
162
 
166
- if existing_text == new_text
167
- puts "#{routes_file} unchanged."
168
- false
169
- else
170
- File.open(routes_file, 'wb') { |f| f.puts(new_text) }
171
- true
163
+ new_content
172
164
  end
173
- end
174
-
175
- def self.annotate_routes(header, content, where_header_found, options = {})
176
- magic_comments_map, content = extract_magic_comments_from_array(content)
177
- if %w(before top).include?(options[:position_in_routes])
178
- header = header << '' if content.first != ''
179
- magic_comments_map << '' if magic_comments_map.any?
180
- new_content = magic_comments_map + header + content
181
- else
182
- # Ensure we have adequate trailing newlines at the end of the file to
183
- # ensure a blank line separating the content from the annotation.
184
- content << '' unless content.last == ''
185
-
186
- # We're moving something from the top of the file to the bottom, so ditch
187
- # the spacer we put in the first time around.
188
- content.shift if where_header_found == :before && content.first == ''
189
165
 
190
- new_content = magic_comments_map + content + header
191
- end
166
+ def app_routes_map(options)
167
+ routes_map = `rake routes`.chomp("\n").split(/\n/, -1)
192
168
 
193
- new_content
194
- end
169
+ # In old versions of Rake, the first line of output was the cwd. Not so
170
+ # much in newer ones. We ditch that line if it exists, and if not, we
171
+ # keep the line around.
172
+ routes_map.shift if routes_map.first =~ /^\(in \//
195
173
 
196
- # TODO: write the method doc using ruby rdoc formats
197
- # where_header_found => This will either be :before, :after, or
198
- # a number. If the number is > 0, the
199
- # annotation was found somewhere in the
200
- # middle of the file. If the number is
201
- # zero, no annotation was found.
202
- def self.strip_annotations(content)
203
- real_content = []
204
- mode = :content
205
- header_found_at = 0
206
-
207
- content.split(/\n/, -1).each_with_index do |line, line_number|
208
- if mode == :header && line !~ /\s*#/
209
- mode = :content
210
- real_content << line unless line.blank?
211
- elsif mode == :content
212
- if line =~ /^\s*#\s*== Route.*$/
213
- header_found_at = line_number + 1 # index start's at 0
214
- mode = :header
215
- else
216
- real_content << line
217
- end
174
+ # Skip routes which match given regex
175
+ # Note: it matches the complete line (route_name, path, controller/action)
176
+ if options[:ignore_routes]
177
+ routes_map.reject! { |line| line =~ /#{options[:ignore_routes]}/ }
218
178
  end
219
- end
220
179
 
221
- where_header_found(real_content, header_found_at)
222
- end
223
-
224
- def self.where_header_found(real_content, header_found_at)
225
- # By default assume the annotation was found in the middle of the file
226
-
227
- # ... unless we have evidence it was at the beginning ...
228
- return real_content, :before if header_found_at == 1
180
+ routes_map
181
+ end
229
182
 
230
- # ... or that it was at the end.
231
- return real_content, :after if header_found_at >= real_content.count
183
+ def content(line, maxs, options = {})
184
+ return line.rstrip unless options[:format_markdown]
232
185
 
233
- # and the default
234
- return real_content, header_found_at
235
- end
186
+ line.each_with_index.map do |elem, index|
187
+ min_length = maxs.map { |arr| arr[index] }.max || 0
236
188
 
237
- def self.strip_on_removal(content, where_header_found)
238
- if where_header_found == :before
239
- content.shift while content.first == ''
240
- elsif where_header_found == :after
241
- content.pop while content.last == ''
189
+ sprintf("%-#{min_length}.#{min_length}s", elem.tr('|', '-'))
190
+ end.join(' | ')
242
191
  end
243
-
244
- # TODO: If the user buried it in the middle, we should probably see about
245
- # TODO: preserving a single line of space between the content above and
246
- # TODO: below...
247
- content
248
192
  end
249
193
  end
@@ -0,0 +1,69 @@
1
+ module AnnotateRoutes
2
+ module Helpers
3
+ MAGIC_COMMENT_MATCHER = Regexp.new(/(^#\s*encoding:.*)|(^# coding:.*)|(^# -\*- coding:.*)|(^# -\*- encoding\s?:.*)|(^#\s*frozen_string_literal:.+)|(^# -\*- frozen_string_literal\s*:.+-\*-)/).freeze
4
+
5
+ class << self
6
+ # TODO: write the method doc using ruby rdoc formats
7
+ # This method returns an array of 'real_content' and 'header_position'.
8
+ # 'header_position' will either be :before, :after, or
9
+ # a number. If the number is > 0, the
10
+ # annotation was found somewhere in the
11
+ # middle of the file. If the number is
12
+ # zero, no annotation was found.
13
+ def strip_annotations(content)
14
+ real_content = []
15
+ mode = :content
16
+ header_position = 0
17
+
18
+ content.split(/\n/, -1).each_with_index do |line, line_number|
19
+ if mode == :header && line !~ /\s*#/
20
+ mode = :content
21
+ real_content << line unless line.blank?
22
+ elsif mode == :content
23
+ if line =~ /^\s*#\s*== Route.*$/
24
+ header_position = line_number + 1 # index start's at 0
25
+ mode = :header
26
+ else
27
+ real_content << line
28
+ end
29
+ end
30
+ end
31
+
32
+ real_content_and_header_position(real_content, header_position)
33
+ end
34
+
35
+ # @param [Array<String>] content
36
+ # @return [Array<String>] all found magic comments
37
+ # @return [Array<String>] content without magic comments
38
+ def extract_magic_comments_from_array(content_array)
39
+ magic_comments = []
40
+ new_content = []
41
+
42
+ content_array.each do |row|
43
+ if row =~ MAGIC_COMMENT_MATCHER
44
+ magic_comments << row.strip
45
+ else
46
+ new_content << row
47
+ end
48
+ end
49
+
50
+ [magic_comments, new_content]
51
+ end
52
+
53
+ private
54
+
55
+ def real_content_and_header_position(real_content, header_position)
56
+ # By default assume the annotation was found in the middle of the file
57
+
58
+ # ... unless we have evidence it was at the beginning ...
59
+ return real_content, :before if header_position == 1
60
+
61
+ # ... or that it was at the end.
62
+ return real_content, :after if header_position >= real_content.count
63
+
64
+ # and the default
65
+ return real_content, header_position
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,38 @@
1
+ module Annotate
2
+ module Constants
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
37
+ end
38
+ end