annotate 2.7.0 → 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.
@@ -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