annotate 3.0.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- return unless routes_exists?
30
- existing_text = File.read(routes_file)
31
-
32
- if rewrite_contents_with_header(existing_text, header(options), options)
33
- puts "#{routes_file} annotated."
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
- return unless routes_exists?
39
- existing_text = File.read(routes_file)
40
- content, where_header_found = strip_annotations(existing_text)
41
- new_content = strip_on_removal(content, where_header_found)
42
- if rewrite_contents(existing_text, new_content)
43
- puts "Removed annotations from #{routes_file}."
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 routes_exists?
50
- routes_exists = File.exists?(routes_file)
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 rewrite_contents_with_header(existing_text, header, options = {})
61
- content, where_header_found = strip_annotations(existing_text)
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 where_header_found == :after
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
- # @param [String, Array<String>]
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, where_header_found, options = {})
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 where_header_found == :before && content.first == ''
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
- new_content
187
- end
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
- def magic_comment_matcher
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
@@ -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