annotate 2.7.4 → 3.1.1

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.
@@ -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