annotate 2.7.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # rubocop:disable Metrics/ModuleLength
2
+
1
3
  # == Annotate Routes
2
4
  #
3
5
  # Based on:
@@ -8,7 +10,7 @@
8
10
  # Yes, it's simple but I'm thick and often need a reminder of what my routes
9
11
  # mean.
10
12
  #
11
- # 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
12
14
  # task. Best to back up your routes file before running:
13
15
  #
14
16
  # Author:
@@ -17,140 +19,175 @@
17
19
  #
18
20
  # Released under the same license as Ruby. No Support. No Warranty.
19
21
  #
22
+
23
+ require_relative './annotate_routes/helpers'
24
+
20
25
  module AnnotateRoutes
21
- PREFIX = "# == Route Map"
22
-
23
- def self.do_annotations(options={})
24
- return unless(routes_exists?)
25
-
26
- position_after = ! %w(before top).include?(options[:position_in_routes])
27
-
28
- routes_map = `rake routes`.split(/\n/, -1)
29
-
30
- # In old versions of Rake, the first line of output was the cwd. Not so
31
- # much in newer ones. We ditch that line if it exists, and if not, we
32
- # keep the line around.
33
- routes_map.shift if(routes_map.first =~ /^\(in \//)
34
-
35
- header = [
36
- "#{PREFIX}" + (options[:timestamp] ? " (Updated #{Time.now.strftime("%Y-%m-%d %H:%M")})" : ""),
37
- "#"
38
- ] + routes_map.map { |line| "# #{line}".rstrip }
39
-
40
- existing_text = File.read(routes_file)
41
- (content, where_header_found) = strip_annotations(existing_text)
42
- changed = where_header_found != 0 # This will either be :before, :after, or
43
- # a number. If the number is > 0, the
44
- # annotation was found somewhere in the
45
- # middle of the file. If the number is
46
- # zero, no annotation was found.
47
-
48
- if(position_after)
49
- # Ensure we have adequate trailing newlines at the end of the file to
50
- # ensure a blank line separating the content from the annotation.
51
- content << '' if(content.last != '')
52
-
53
- # We're moving something from the top of the file to the bottom, so ditch
54
- # the spacer we put in the first time around.
55
- if(changed && where_header_found == :before)
56
- content.shift if(content.first == '')
26
+ PREFIX = '== Route Map'.freeze
27
+ PREFIX_MD = '## Route Map'.freeze
28
+ HEADER_ROW = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action'].freeze
29
+
30
+ class << self
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
47
+
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."
57
61
  end
58
- else
59
- header = header << '' if(content.first != '' || changed)
60
62
  end
61
63
 
62
- content = position_after ? (content + header) : header + content
64
+ private
63
65
 
64
- if write_contents(existing_text, content)
65
- puts "#{routes_file} annotated."
66
- else
67
- puts "#{routes_file} unchanged."
66
+ def routes_file_exist?
67
+ File.exist?(routes_file)
68
68
  end
69
- end
70
69
 
71
- def self.remove_annotations(options={})
72
- return unless(routes_exists?)
73
- existing_text = File.read(routes_file)
74
- (content, where_header_found) = strip_annotations(existing_text)
70
+ def routes_file
71
+ @routes_rb ||= File.join('config', 'routes.rb')
72
+ end
73
+
74
+ def header(options = {})
75
+ routes_map = app_routes_map(options)
76
+
77
+ magic_comments_map, routes_map = Helpers.extract_magic_comments_from_array(routes_map)
78
+
79
+ out = []
80
+
81
+ magic_comments_map.each do |magic_comment|
82
+ out << magic_comment
83
+ end
84
+ out << '' if magic_comments_map.any?
85
+
86
+ out << comment(options[:wrapper_open]) if options[:wrapper_open]
87
+
88
+ out << comment(options[:format_markdown] ? PREFIX_MD : PREFIX) + (options[:timestamp] ? " (Updated #{Time.now.strftime('%Y-%m-%d %H:%M')})" : '')
89
+ out << comment
90
+ return out if routes_map.size.zero?
91
+
92
+ maxs = [HEADER_ROW.map(&:size)] + routes_map[1..-1].map { |line| line.split.map(&:size) }
93
+
94
+ if options[:format_markdown]
95
+ max = maxs.map(&:max).compact.max
96
+
97
+ out << comment(content(HEADER_ROW, maxs, options))
98
+ out << comment(content(['-' * max, '-' * max, '-' * max, '-' * max], maxs, options))
99
+ else
100
+ out << comment(content(routes_map[0], maxs, options))
101
+ end
75
102
 
76
- content = strip_on_removal(content, where_header_found)
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]
77
105
 
78
- if write_contents(existing_text, content)
79
- puts "Removed annotations from #{routes_file}."
80
- else
81
- puts "#{routes_file} unchanged."
106
+ out
82
107
  end
83
- end
84
108
 
85
- protected
109
+ def comment(row = '')
110
+ if row == ''
111
+ '#'
112
+ else
113
+ "# #{row}"
114
+ end
115
+ end
86
116
 
87
- def self.routes_file
88
- @routes_rb ||= File.join("config", "routes.rb")
89
- end
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 == ''
122
+ end
90
123
 
91
- def self.routes_exists?
92
- routes_exists = File.exists?(routes_file)
93
- puts "Can`t find routes.rb" if(!routes_exists)
94
- return routes_exists
95
- end
124
+ # Make sure we end on a trailing newline.
125
+ content << '' unless content.last == ''
126
+
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
132
+
133
+ def rewrite_contents(existing_text, new_text)
134
+ if existing_text == new_text
135
+ false
136
+ else
137
+ File.open(routes_file, 'wb') { |f| f.puts(new_text) }
138
+ true
139
+ end
140
+ end
96
141
 
97
- def self.write_contents(existing_text, new_content)
98
- # Make sure we end on a trailing newline.
99
- new_content << '' unless(new_content.last == '')
100
- new_text = new_content.join("\n")
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 == ''
152
+
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 == ''
156
+
157
+ new_content = magic_comments_map + content + header
158
+ end
101
159
 
102
- return false if existing_text == new_text
160
+ # Make sure we end on a trailing newline.
161
+ new_content << '' unless new_content.last == ''
103
162
 
104
- File.open(routes_file, "wb") { |f| f.puts(new_text) }
105
- return true
106
- end
163
+ new_content
164
+ end
107
165
 
108
- def self.strip_annotations(content)
109
- real_content = []
110
- mode = :content
111
- line_number = 0
112
- header_found_at = 0
113
- content.split(/\n/, -1).each do |line|
114
- line_number += 1
115
- begin
116
- if(mode == :header)
117
- if(line !~ /\s*#/)
118
- mode = :content
119
- raise unless (line == '')
120
- end
121
- elsif(mode == :content)
122
- if(line =~ /^\s*#\s*== Route.*$/)
123
- header_found_at = line_number
124
- mode = :header
125
- else
126
- real_content << line
127
- end
128
- end
129
- rescue
130
- retry
166
+ def app_routes_map(options)
167
+ routes_map = `rake routes`.chomp("\n").split(/\n/, -1)
168
+
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 \//
173
+
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]}/ }
131
178
  end
179
+
180
+ routes_map
132
181
  end
133
- content_lines = real_content.count
134
182
 
135
- # By default assume the annotation was found in the middle of the file...
136
- where_header_found = header_found_at
137
- # ... unless we have evidence it was at the beginning ...
138
- where_header_found = :before if(header_found_at == 1)
139
- # ... or that it was at the end.
140
- where_header_found = :after if(header_found_at >= content_lines)
183
+ def content(line, maxs, options = {})
184
+ return line.rstrip unless options[:format_markdown]
141
185
 
142
- return real_content, where_header_found
143
- end
186
+ line.each_with_index.map do |elem, index|
187
+ min_length = maxs.map { |arr| arr[index] }.max || 0
144
188
 
145
- def self.strip_on_removal(content, where_header_found)
146
- if(where_header_found == :before)
147
- content.shift while(content.first == '')
148
- elsif(where_header_found == :after)
149
- content.pop while(content.last == '')
189
+ sprintf("%-#{min_length}.#{min_length}s", elem.tr('|', '-'))
190
+ end.join(' | ')
150
191
  end
151
- # TODO: If the user buried it in the middle, we should probably see about
152
- # TODO: preserving a single line of space between the content above and
153
- # TODO: below...
154
- return content
155
192
  end
156
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
@@ -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
@@ -0,0 +1,303 @@
1
+ require 'optparse'
2
+
3
+ module Annotate
4
+ # Class for handling command line arguments
5
+ class Parser # rubocop:disable Metrics/ClassLength
6
+ def self.parse(args, env = {})
7
+ new(args, env).parse
8
+ end
9
+
10
+ attr_reader :args, :options, :env
11
+
12
+ DEFAULT_OPTIONS = {
13
+ target_action: :do_annotations,
14
+ exit: false
15
+ }.freeze
16
+
17
+ ANNOTATION_POSITIONS = %w[before top after bottom].freeze
18
+ FILE_TYPE_POSITIONS = %w[position_in_class position_in_factory position_in_fixture position_in_test position_in_routes position_in_serializer].freeze
19
+ EXCLUSION_LIST = %w[tests fixtures factories serializers].freeze
20
+ FORMAT_TYPES = %w[bare rdoc yard markdown].freeze
21
+
22
+ def initialize(args, env)
23
+ @args = args
24
+ @options = DEFAULT_OPTIONS.dup
25
+ @env = env
26
+ end
27
+
28
+ def parse
29
+ # To split up because right now this method parses and commits
30
+ parser.parse!(args)
31
+
32
+ commit
33
+
34
+ options
35
+ end
36
+
37
+ private
38
+
39
+ def parser
40
+ OptionParser.new do |option_parser|
41
+ add_options_to_parser(option_parser)
42
+ end
43
+ end
44
+
45
+ def commit
46
+ env.each_pair do |key, value|
47
+ ENV[key] = value
48
+ end
49
+ end
50
+
51
+ def add_options_to_parser(option_parser) # rubocop:disable Metrics/MethodLength
52
+ has_set_position = {}
53
+
54
+ option_parser.banner = 'Usage: annotate [options] [model_file]*'
55
+
56
+ option_parser.on('--additional-file-patterns path1,path2,path3',
57
+ Array,
58
+ "Additional file paths or globs to annotate, separated by commas (e.g. `/foo/bar/%model_name%/*.rb,/baz/%model_name%.rb`)") do |additional_file_patterns|
59
+ ENV['additional_file_patterns'] = additional_file_patterns
60
+ end
61
+
62
+ option_parser.on('-d',
63
+ '--delete',
64
+ 'Remove annotations from all model files or the routes.rb file') do
65
+ @options[:target_action] = :remove_annotations
66
+ end
67
+
68
+ option_parser.on('-p',
69
+ '--position [before|top|after|bottom]',
70
+ ANNOTATION_POSITIONS,
71
+ 'Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/route/serializer file(s)') do |position|
72
+ env['position'] = position
73
+
74
+ FILE_TYPE_POSITIONS.each do |key|
75
+ env[key] = position unless has_set_position[key]
76
+ end
77
+ end
78
+
79
+ option_parser.on('--pc',
80
+ '--position-in-class [before|top|after|bottom]',
81
+ ANNOTATION_POSITIONS,
82
+ 'Place the annotations at the top (before) or the bottom (after) of the model file') do |position_in_class|
83
+ env['position_in_class'] = position_in_class
84
+ has_set_position['position_in_class'] = true
85
+ end
86
+
87
+ option_parser.on('--pf',
88
+ '--position-in-factory [before|top|after|bottom]',
89
+ ANNOTATION_POSITIONS,
90
+ 'Place the annotations at the top (before) or the bottom (after) of any factory files') do |position_in_factory|
91
+ env['position_in_factory'] = position_in_factory
92
+ has_set_position['position_in_factory'] = true
93
+ end
94
+
95
+ option_parser.on('--px',
96
+ '--position-in-fixture [before|top|after|bottom]',
97
+ ANNOTATION_POSITIONS,
98
+ 'Place the annotations at the top (before) or the bottom (after) of any fixture files') do |position_in_fixture|
99
+ env['position_in_fixture'] = position_in_fixture
100
+ has_set_position['position_in_fixture'] = true
101
+ end
102
+
103
+ option_parser.on('--pt',
104
+ '--position-in-test [before|top|after|bottom]',
105
+ ANNOTATION_POSITIONS,
106
+ 'Place the annotations at the top (before) or the bottom (after) of any test files') do |position_in_test|
107
+ env['position_in_test'] = position_in_test
108
+ has_set_position['position_in_test'] = true
109
+ end
110
+
111
+ option_parser.on('--pr',
112
+ '--position-in-routes [before|top|after|bottom]',
113
+ ANNOTATION_POSITIONS,
114
+ 'Place the annotations at the top (before) or the bottom (after) of the routes.rb file') do |position_in_routes|
115
+ env['position_in_routes'] = position_in_routes
116
+ has_set_position['position_in_routes'] = true
117
+ end
118
+
119
+ option_parser.on('--ps',
120
+ '--position-in-serializer [before|top|after|bottom]',
121
+ ANNOTATION_POSITIONS,
122
+ 'Place the annotations at the top (before) or the bottom (after) of the serializer files') do |position_in_serializer|
123
+ env['position_in_serializer'] = position_in_serializer
124
+ has_set_position['position_in_serializer'] = true
125
+ end
126
+
127
+ option_parser.on('--w',
128
+ '--wrapper STR',
129
+ 'Wrap annotation with the text passed as parameter.',
130
+ 'If --w option is used, the same text will be used as opening and closing') do |wrapper|
131
+ env['wrapper'] = wrapper
132
+ end
133
+
134
+ option_parser.on('--wo',
135
+ '--wrapper-open STR',
136
+ 'Annotation wrapper opening.') do |wrapper_open|
137
+ env['wrapper_open'] = wrapper_open
138
+ end
139
+
140
+ option_parser.on('--wc',
141
+ '--wrapper-close STR',
142
+ 'Annotation wrapper closing') do |wrapper_close|
143
+ env['wrapper_close'] = wrapper_close
144
+ end
145
+
146
+ option_parser.on('-r',
147
+ '--routes',
148
+ "Annotate routes.rb with the output of 'rake routes'") do
149
+ env['routes'] = 'true'
150
+ end
151
+
152
+ option_parser.on('--models',
153
+ "Annotate ActiveRecord models") do
154
+ env['models'] = 'true'
155
+ end
156
+
157
+ option_parser.on('-a',
158
+ '--active-admin',
159
+ 'Annotate active_admin models') do
160
+ env['active_admin'] = 'true'
161
+ end
162
+
163
+ option_parser.on('-v',
164
+ '--version',
165
+ 'Show the current version of this gem') do
166
+ puts "annotate v#{Annotate.version}"
167
+ @options[:exit] = true
168
+ end
169
+
170
+ option_parser.on('-m',
171
+ '--show-migration',
172
+ 'Include the migration version number in the annotation') do
173
+ env['include_version'] = 'yes'
174
+ end
175
+
176
+ option_parser.on('-k',
177
+ '--show-foreign-keys',
178
+ "List the table's foreign key constraints in the annotation") do
179
+ env['show_foreign_keys'] = 'yes'
180
+ end
181
+
182
+ option_parser.on('--ck',
183
+ '--complete-foreign-keys',
184
+ 'Complete foreign key names in the annotation') do
185
+ env['show_foreign_keys'] = 'yes'
186
+ env['show_complete_foreign_keys'] = 'yes'
187
+ end
188
+
189
+ option_parser.on('-i',
190
+ '--show-indexes',
191
+ "List the table's database indexes in the annotation") do
192
+ env['show_indexes'] = 'yes'
193
+ end
194
+
195
+ option_parser.on('-s',
196
+ '--simple-indexes',
197
+ "Concat the column's related indexes in the annotation") do
198
+ env['simple_indexes'] = 'yes'
199
+ end
200
+
201
+ option_parser.on('--model-dir dir',
202
+ "Annotate model files stored in dir rather than app/models, separate multiple dirs with commas") do |dir|
203
+ env['model_dir'] = dir
204
+ end
205
+
206
+ option_parser.on('--root-dir dir',
207
+ "Annotate files stored within root dir projects, separate multiple dirs with commas") do |dir|
208
+ env['root_dir'] = dir
209
+ end
210
+
211
+ option_parser.on('--ignore-model-subdirects',
212
+ "Ignore subdirectories of the models directory") do
213
+ env['ignore_model_sub_dir'] = 'yes'
214
+ end
215
+
216
+ option_parser.on('--sort',
217
+ "Sort columns alphabetically, rather than in creation order") do
218
+ env['sort'] = 'yes'
219
+ end
220
+
221
+ option_parser.on('--classified-sort',
222
+ "Sort columns alphabetically, but first goes id, then the rest columns, then the timestamp columns and then the association columns") do
223
+ env['classified_sort'] = 'yes'
224
+ end
225
+
226
+ option_parser.on('-R',
227
+ '--require path',
228
+ "Additional file to require before loading models, may be used multiple times") do |path|
229
+ env['require'] = if env['require'].present?
230
+ "#{env['require']},#{path}"
231
+ else
232
+ path
233
+ end
234
+ end
235
+
236
+ option_parser.on('-e',
237
+ '--exclude [tests,fixtures,factories,serializers]',
238
+ Array,
239
+ "Do not annotate fixtures, test files, factories, and/or serializers") do |exclusions|
240
+ exclusions ||= EXCLUSION_LIST
241
+ exclusions.each { |exclusion| env["exclude_#{exclusion}"] = 'yes' }
242
+ end
243
+
244
+ option_parser.on('-f',
245
+ '--format [bare|rdoc|yard|markdown]',
246
+ FORMAT_TYPES,
247
+ 'Render Schema Infomation as plain/RDoc/Yard/Markdown') do |format_type|
248
+ env["format_#{format_type}"] = 'yes'
249
+ end
250
+
251
+ option_parser.on('--force',
252
+ 'Force new annotations even if there are no changes.') do
253
+ env['force'] = 'yes'
254
+ end
255
+
256
+ option_parser.on('--frozen',
257
+ 'Do not allow to change annotations. Exits non-zero if there are going to be changes to files.') do
258
+ env['frozen'] = 'yes'
259
+ end
260
+
261
+ option_parser.on('--timestamp',
262
+ 'Include timestamp in (routes) annotation') do
263
+ env['timestamp'] = 'true'
264
+ end
265
+
266
+ option_parser.on('--trace',
267
+ 'If unable to annotate a file, print the full stack trace, not just the exception message.') do
268
+ env['trace'] = 'yes'
269
+ end
270
+
271
+ option_parser.on('-I',
272
+ '--ignore-columns REGEX',
273
+ "don't annotate columns that match a given REGEX (i.e., `annotate -I '^(id|updated_at|created_at)'`") do |regex|
274
+ env['ignore_columns'] = regex
275
+ end
276
+
277
+ option_parser.on('--ignore-routes REGEX',
278
+ "don't annotate routes that match a given REGEX (i.e., `annotate -I '(mobile|resque|pghero)'`") do |regex|
279
+ env['ignore_routes'] = regex
280
+ end
281
+
282
+ option_parser.on('--hide-limit-column-types VALUES',
283
+ "don't show limit for given column types, separated by commas (i.e., `integer,boolean,text`)") do |values|
284
+ env['hide_limit_column_types'] = values.to_s
285
+ end
286
+
287
+ option_parser.on('--hide-default-column-types VALUES',
288
+ "don't show default for given column types, separated by commas (i.e., `json,jsonb,hstore`)") do |values|
289
+ env['hide_default_column_types'] = values.to_s
290
+ end
291
+
292
+ option_parser.on('--ignore-unknown-models',
293
+ "don't display warnings for bad model files") do
294
+ env['ignore_unknown_models'] = 'true'
295
+ end
296
+
297
+ option_parser.on('--with-comment',
298
+ "include database comments in model annotations") do
299
+ env['with_comment'] = 'true'
300
+ end
301
+ end
302
+ end
303
+ end