-
Notifications
You must be signed in to change notification settings - Fork 81
Expand file tree
/
Copy pathparser.rb
More file actions
452 lines (374 loc) · 15.5 KB
/
parser.rb
File metadata and controls
452 lines (374 loc) · 15.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
require "optparse"
module AnnotateRb
# Class for handling command line arguments
class Parser # rubocop:disable Metrics/ClassLength
def self.parse(args, existing_options)
new(args, existing_options).parse
end
BANNER_STRING = <<~BANNER.freeze
Usage: annotaterb [command] [options]
Commands:
models [options]
routes [options]
help
version
BANNER
DEFAULT_OPTIONS = {
target_action: :do_annotations,
exit: false
}.freeze
ANNOTATION_POSITIONS = %w[before top after bottom].freeze
FILE_TYPE_POSITIONS = %w[position_in_class position_in_factory position_in_fixture position_in_test position_in_routes position_in_serializer].freeze
EXCLUSION_LIST = %w[tests fixtures factories serializers].freeze
FORMAT_TYPES = %w[bare rdoc yard markdown].freeze
COMMAND_MAP = {
"models" => :models,
"routes" => :routes,
"version" => :version,
"help" => :help
}.freeze
def initialize(args, existing_options)
@args = args.clone
base_options = DEFAULT_OPTIONS.dup
@options = base_options.merge(existing_options)
@commands = []
@options[:original_args] = args.clone
end
def parse
parse_command(@args)
parser.parse!(@args)
act_on_command
@options
end
def remaining_args
# `@args` gets modified throughout the lifecycle of this class.
# It starts as a shallow clone of ARGV, then arguments matching commands and options are removed in #parse
@args
end
private
def parse_command(args)
command_arg = args.first
command = COMMAND_MAP[command_arg]
if command
args.shift
@commands << command
end
end
def act_on_command
map = {
models: Commands::AnnotateModels.new,
routes: Commands::AnnotateRoutes.new,
help: Commands::PrintHelp.new(@parser),
version: Commands::PrintVersion.new
}
if @commands.size > 1
warn "Only one command can be run at a time"
end
@options[:command] = if @commands.any?
map[@commands.first]
else # None
map[:help]
end
end
def parser
@parser ||= OptionParser.new do |option_parser|
option_parser.banner = BANNER_STRING
# ------------------------------------------------------------------------------------------------------------=
option_parser.separator("")
option_parser.separator("Options:")
option_parser.on("-v", "--version", "Display the version..") do
@commands << :version
end
option_parser.on("-h", "--help", "You're looking at it.") do
@commands << :help
end
add_model_options_to_parser(option_parser)
add_route_options_to_parser(option_parser)
add_wrapper_options_to_parser(option_parser)
add_options_to_parser(option_parser)
add_position_options_to_parser(option_parser)
add_utils_to_parser(option_parser)
end
end
def add_wrapper_options_to_parser(option_parser)
option_parser.on("--w",
"--wrapper STR",
"Wrap annotation with the text passed as parameter.",
"If --w option is used, the same text will be used as opening and closing") do |wrapper|
@options[:wrapper] = wrapper
end
option_parser.on("--wo",
"--wrapper-open STR",
"Annotation wrapper opening.") do |wrapper_open|
@options[:wrapper_open] = wrapper_open
end
option_parser.on("--wc",
"--wrapper-close STR",
"Annotation wrapper closing") do |wrapper_close|
@options[:wrapper_close] = wrapper_close
end
end
def add_utils_to_parser(option_parser)
option_parser.on("--force",
"Force new annotations even if there are no changes.") do
@options[:force] = true
end
option_parser.on("--debug",
"Prints the options and outputs messages to make it easier to debug.") do
@options[:debug] = true
end
option_parser.on("--frozen",
"Do not allow to change annotations. Exits non-zero if there are going to be changes to files.") do
@options[:frozen] = true
end
option_parser.on("--trace",
"If unable to annotate a file, print the full stack trace, not just the exception message.") do
@options[:trace] = true
end
end
def add_model_options_to_parser(option_parser)
option_parser.separator("")
option_parser.separator("Annotate model options:")
option_parser.separator(" " * 4 + "Usage: annotaterb models [options]")
option_parser.separator("")
option_parser.on("-a",
"--active-admin",
"Annotate active_admin models") do
@options[:active_admin] = true
end
option_parser.on("--show-migration",
"Include the migration version number in the annotation") do
@options[:include_version] = true
end
option_parser.on("-k",
"--show-foreign-keys",
"List the table's foreign key constraints in the annotation") do
@options[:show_foreign_keys] = true
end
option_parser.on("--ck",
"--complete-foreign-keys",
"Complete foreign key names in the annotation") do
@options[:show_foreign_keys] = true
@options[:show_complete_foreign_keys] = true
end
option_parser.on("-i",
"--show-indexes",
"List the table's database indexes in the annotation") do
@options[:show_indexes] = true
end
option_parser.on("-s",
"--simple-indexes",
"Concat the column's related indexes in the annotation") do
@options[:simple_indexes] = true
end
option_parser.on("-c",
"--show-check-constraints",
"List the table's check constraints in the annotation") do
@options[:show_check_constraints] = true
end
option_parser.on("--hide-limit-column-types VALUES",
"don't show limit for given column types, separated by commas (i.e., `integer,boolean,text`)") do |values|
@options[:hide_limit_column_types] = values.to_s
end
option_parser.on("--hide-default-column-types VALUES",
"don't show default for given column types, separated by commas (i.e., `json,jsonb,hstore`)") do |values|
@options[:hide_default_column_types] = values.to_s
end
option_parser.on("--ignore-unknown-models",
"don't display warnings for bad model files") do
@options[:ignore_unknown_models] = true
end
option_parser.on("-I",
"--ignore-columns REGEX",
"don't annotate columns that match a given REGEX (i.e., `annotate -I '^(id|updated_at|created_at)'`") do |regex|
@options[:ignore_columns] = regex
end
option_parser.on("--with-comment",
"include database comments in model annotations") do
@options[:with_comment] = true
end
option_parser.on("--without-comment",
"include database comments in model annotations") do
@options[:with_comment] = false
end
option_parser.on("--with-column-comments",
"include column comments in model annotations") do
@options[:with_column_comments] = true
end
option_parser.on("--without-column-comments",
"exclude column comments in model annotations") do
@options[:with_column_comments] = false
end
option_parser.on("--position-of-column-comment [with_name|rightmost_column]",
"set the position, in the annotation block, of the column comment") do |value|
@options[:position_of_column_comment] = value.to_sym
end
option_parser.on("--with-table-comments",
"include table comments in model annotations") do
@options[:with_table_comments] = true
end
option_parser.on("--without-table-comments",
"exclude table comments in model annotations") do
@options[:with_table_comments] = false
end
option_parser.on("--classes-default-to-s class",
"Custom classes to be represented with `to_s`, may be used multiple times") do |klass|
@options[:classes_default_to_s] = if @options[:classes_default_to_s].present?
[*@options[:classes_default_to_s], klass]
else
[klass]
end
end
option_parser.on("--nested-position",
"Place annotations directly above nested classes or modules instead of at the top of the file.") do
@options[:nested_position] = true
end
end
def add_route_options_to_parser(option_parser)
option_parser.separator("")
option_parser.separator("Annotate routes options:")
option_parser.separator(" " * 4 + "Usage: annotaterb routes [options]")
option_parser.separator("")
option_parser.on("--ignore-routes REGEX",
"don't annotate routes that match a given REGEX (i.e., `annotate -I '(mobile|resque|pghero)'`") do |regex|
@options[:ignore_routes] = regex
end
option_parser.on("--timestamp",
"Include timestamp in (routes) annotation") do
@options[:timestamp] = true
end
end
def add_position_options_to_parser(option_parser)
has_set_position = {}
option_parser.on("-p",
"--position [before|top|after|bottom]",
ANNOTATION_POSITIONS,
"Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/route/serializer file(s)") do |position|
@options[:position] = position
FILE_TYPE_POSITIONS.each do |key|
@options[key.to_sym] = position unless has_set_position[key]
end
end
option_parser.on("--pc",
"--position-in-class [before|top|after|bottom]",
ANNOTATION_POSITIONS,
"Place the annotations at the top (before) or the bottom (after) of the model file") do |position_in_class|
@options[:position_in_class] = position_in_class
has_set_position["position_in_class"] = true
end
option_parser.on("--pf",
"--position-in-factory [before|top|after|bottom]",
ANNOTATION_POSITIONS,
"Place the annotations at the top (before) or the bottom (after) of any factory files") do |position_in_factory|
@options[:position_in_factory] = position_in_factory
has_set_position["position_in_factory"] = true
end
option_parser.on("--px",
"--position-in-fixture [before|top|after|bottom]",
ANNOTATION_POSITIONS,
"Place the annotations at the top (before) or the bottom (after) of any fixture files") do |position_in_fixture|
@options[:position_in_fixture] = position_in_fixture
has_set_position["position_in_fixture"] = true
end
option_parser.on("--pt",
"--position-in-test [before|top|after|bottom]",
ANNOTATION_POSITIONS,
"Place the annotations at the top (before) or the bottom (after) of any test files") do |position_in_test|
@options[:position_in_test] = position_in_test
has_set_position["position_in_test"] = true
end
option_parser.on("--pr",
"--position-in-routes [before|top|after|bottom]",
ANNOTATION_POSITIONS,
"Place the annotations at the top (before) or the bottom (after) of the routes.rb file") do |position_in_routes|
@options[:position_in_routes] = position_in_routes
has_set_position["position_in_routes"] = true
end
option_parser.on("--ps",
"--position-in-serializer [before|top|after|bottom]",
ANNOTATION_POSITIONS,
"Place the annotations at the top (before) or the bottom (after) of the serializer files") do |position_in_serializer|
@options[:position_in_serializer] = position_in_serializer
has_set_position["position_in_serializer"] = true
end
option_parser.on("--pa",
"--position-in-additional-file-patterns [before|top|after|bottom]",
ANNOTATION_POSITIONS,
"Place the annotations at the top (before) or the bottom (after) of files captured in additional file patterns") do |position_in_serializer|
@options[:position_in_additional_file_patterns] = position_in_serializer
has_set_position["position_in_additional_file_patterns"] = true
end
end
def add_options_to_parser(option_parser) # rubocop:disable Metrics/MethodLength
option_parser.separator("")
option_parser.separator("Command options:")
option_parser.separator("Additional options that work for annotating models and routes")
option_parser.separator("")
option_parser.on("--additional-file-patterns path1,path2,path3",
Array,
"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|
@options[:additional_file_patterns] = additional_file_patterns
end
option_parser.on("--only-file-patterns path1,path2,path3",
Array,
"If given, restrict annotated models to listed patterns, separated by commas (e.g. `*session*.rb,*login*.rb`)") do |only_file_patterns|
@options[:only_file_patterns] = only_file_patterns
end
option_parser.on("-d",
"--delete",
"Remove annotations from all model files or the routes.rb file") do
@options[:target_action] = :remove_annotations
end
option_parser.on("--model-dir dir",
"Annotate model files stored in dir rather than app/models, separate multiple dirs with commas") do |dir|
@options[:model_dir] = dir
end
option_parser.on("--root-dir dir",
"Annotate files stored within root dir projects, separate multiple dirs with commas") do |dir|
@options[:root_dir] = dir
end
option_parser.on("--ignore-model-subdirects",
"Ignore subdirectories of the models directory") do
@options[:ignore_model_sub_dir] = true
end
option_parser.on("--sort",
"Sort columns alphabetically, rather than in creation order") do
@options[:sort] = true
end
option_parser.on("--classified-sort",
"Sort columns alphabetically, but first goes id, then the rest columns, then the timestamp columns and then the association columns") do
@options[:classified_sort] = true
end
option_parser.on("--grouped-polymorphic",
"Group polymorphic associations together in the annotation when using --classified-sort") do
@options[:grouped_polymorphic] = true
end
option_parser.on("-R",
"--require path",
"Additional file to require before loading models, may be used multiple times") do |path|
@options[:require] = if @options[:require].present?
[@options[:require], path].join(",")
else
path
end
end
option_parser.on("-e",
"--exclude [tests,fixtures,factories,serializers,sti_subclasses]",
Array,
"Do not annotate fixtures, test files, factories, serializers, and/or sti subclasses") do |exclusions|
exclusions ||= EXCLUSION_LIST
exclusions.each { |exclusion| @options["exclude_#{exclusion}".to_sym] = true }
end
option_parser.on("-f",
"--format [bare|rdoc|yard|markdown]",
FORMAT_TYPES,
"Render Schema Information as plain/RDoc/Yard/Markdown") do |format_type|
@options["format_#{format_type}".to_sym] = true
end
option_parser.on("--config-path [path]",
"Path to configuration file (by default, .annotaterb.yml in the root of the project)") do |path|
@options[:config_path] = path
end
end
end
end