annotaterb 4.4.1 → 4.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +29 -0
- data/VERSION +1 -1
- data/lib/annotate_rb/config_generator.rb +28 -0
- data/lib/annotate_rb/eager_loader.rb +6 -2
- data/lib/annotate_rb/model_annotator/annotated_file/generator.rb +92 -0
- data/lib/annotate_rb/model_annotator/annotated_file/updater.rb +46 -0
- data/lib/annotate_rb/model_annotator/annotated_file.rb +10 -0
- data/lib/annotate_rb/model_annotator/annotator.rb +2 -2
- data/lib/annotate_rb/model_annotator/file_name_resolver.rb +5 -0
- data/lib/annotate_rb/model_annotator/file_parser/annotation_finder.rb +103 -0
- data/lib/annotate_rb/model_annotator/file_parser/custom_parser.rb +217 -0
- data/lib/annotate_rb/model_annotator/file_parser/parsed_file.rb +94 -0
- data/lib/annotate_rb/model_annotator/file_parser/parsed_file_result.rb +54 -0
- data/lib/annotate_rb/model_annotator/file_parser.rb +12 -0
- data/lib/annotate_rb/model_annotator/model_class_getter.rb +7 -0
- data/lib/annotate_rb/model_annotator/model_files_getter.rb +4 -8
- data/lib/annotate_rb/model_annotator/model_wrapper.rb +11 -2
- data/lib/annotate_rb/model_annotator/pattern_getter.rb +2 -0
- data/lib/annotate_rb/model_annotator/related_files_list_builder.rb +3 -3
- data/lib/annotate_rb/model_annotator/single_file_annotation_remover.rb +10 -12
- data/lib/annotate_rb/model_annotator/single_file_annotator.rb +15 -8
- data/lib/annotate_rb/model_annotator/single_file_annotator_instruction.rb +1 -1
- data/lib/annotate_rb/model_annotator/single_file_remove_annotation_instruction.rb +1 -1
- data/lib/annotate_rb/model_annotator/zeitwerk_class_getter.rb +113 -0
- data/lib/annotate_rb/model_annotator.rb +3 -4
- data/lib/annotate_rb/options.rb +4 -0
- data/lib/annotate_rb/parser.rb +9 -3
- data/lib/annotate_rb/runner.rb +5 -4
- data/lib/annotate_rb/tasks/annotate_models_migrate.rake +5 -0
- data/lib/annotate_rb.rb +1 -0
- data/lib/generators/annotate_rb/config/USAGE +6 -0
- data/lib/generators/annotate_rb/config/config_generator.rb +15 -0
- data/lib/generators/annotate_rb/hook/USAGE +7 -0
- data/lib/generators/annotate_rb/hook/hook_generator.rb +15 -0
- data/lib/generators/annotate_rb/install/install_generator.rb +3 -4
- data/lib/generators/annotate_rb/update_config/USAGE +6 -0
- data/lib/generators/annotate_rb/update_config/update_config_generator.rb +15 -0
- metadata +20 -8
- data/lib/annotate_rb/model_annotator/annotation_pattern_generator.rb +0 -19
- data/lib/annotate_rb/model_annotator/file_builder.rb +0 -57
- data/lib/annotate_rb/model_annotator/file_components.rb +0 -81
- data/lib/annotate_rb/model_annotator/magic_comment_parser.rb +0 -32
- /data/lib/generators/annotate_rb/{install → hook}/templates/annotate_rb.rake +0 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module ModelAnnotator
|
5
|
+
module FileParser
|
6
|
+
class ParsedFile
|
7
|
+
SKIP_ANNOTATION_STRING = "# -*- SkipSchemaAnnotations"
|
8
|
+
|
9
|
+
def initialize(file_content, new_annotations, options)
|
10
|
+
@file_content = file_content
|
11
|
+
@file_lines = @file_content.lines
|
12
|
+
@new_annotations = new_annotations
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse
|
17
|
+
@finder = AnnotationFinder.new(@file_content, @options[:wrapper_open], @options[:wrapper_close])
|
18
|
+
has_annotations = false
|
19
|
+
|
20
|
+
begin
|
21
|
+
@finder.run
|
22
|
+
has_annotations = @finder.annotated?
|
23
|
+
rescue AnnotationFinder::NoAnnotationFound => _e
|
24
|
+
end
|
25
|
+
|
26
|
+
annotations = if has_annotations
|
27
|
+
@file_lines[(@finder.annotation_start)..(@finder.annotation_end)].join
|
28
|
+
else
|
29
|
+
""
|
30
|
+
end
|
31
|
+
|
32
|
+
@diff = AnnotationDiffGenerator.new(annotations, @new_annotations).generate
|
33
|
+
@file_parser = @finder.parser
|
34
|
+
|
35
|
+
has_skip_string = @file_parser.comments.any? { |comment, _lineno| comment.include?(SKIP_ANNOTATION_STRING) }
|
36
|
+
annotations_changed = @diff.changed?
|
37
|
+
|
38
|
+
has_leading_whitespace = false
|
39
|
+
has_trailing_whitespace = false
|
40
|
+
|
41
|
+
annotations_with_whitespace = if has_annotations
|
42
|
+
begin
|
43
|
+
annotation_start = @finder.annotation_start
|
44
|
+
annotation_end = @finder.annotation_end
|
45
|
+
|
46
|
+
if @file_lines[annotation_start - 1]&.strip&.empty?
|
47
|
+
annotation_start -= 1
|
48
|
+
has_leading_whitespace = true
|
49
|
+
end
|
50
|
+
|
51
|
+
if @file_lines[annotation_end + 1]&.strip&.empty?
|
52
|
+
annotation_end += 1
|
53
|
+
has_trailing_whitespace = true
|
54
|
+
end
|
55
|
+
|
56
|
+
@file_lines[annotation_start..annotation_end].join
|
57
|
+
end
|
58
|
+
else
|
59
|
+
""
|
60
|
+
end
|
61
|
+
|
62
|
+
# :before or :after when it's set
|
63
|
+
annotation_position = nil
|
64
|
+
|
65
|
+
if has_annotations
|
66
|
+
const_declaration = @file_parser.starts.first
|
67
|
+
|
68
|
+
# If the file does not have any class or module declaration then const_declaration can be nil
|
69
|
+
_const, line_number = const_declaration
|
70
|
+
|
71
|
+
if line_number
|
72
|
+
annotation_position = if @finder.annotation_start < line_number
|
73
|
+
:before
|
74
|
+
else
|
75
|
+
:after
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
_result = ParsedFileResult.new(
|
81
|
+
has_annotations: has_annotations,
|
82
|
+
has_skip_string: has_skip_string,
|
83
|
+
annotations_changed: annotations_changed,
|
84
|
+
annotations: annotations,
|
85
|
+
annotations_with_whitespace: annotations_with_whitespace,
|
86
|
+
has_leading_whitespace: has_leading_whitespace,
|
87
|
+
has_trailing_whitespace: has_trailing_whitespace,
|
88
|
+
annotation_position: annotation_position
|
89
|
+
)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module ModelAnnotator
|
5
|
+
module FileParser
|
6
|
+
class ParsedFileResult
|
7
|
+
def initialize(
|
8
|
+
has_annotations:,
|
9
|
+
has_skip_string:,
|
10
|
+
annotations_changed:,
|
11
|
+
annotations:,
|
12
|
+
annotations_with_whitespace:,
|
13
|
+
has_leading_whitespace:,
|
14
|
+
has_trailing_whitespace:,
|
15
|
+
annotation_position:
|
16
|
+
)
|
17
|
+
@has_annotations = has_annotations
|
18
|
+
@has_skip_string = has_skip_string
|
19
|
+
@annotations_changed = annotations_changed
|
20
|
+
@annotations = annotations
|
21
|
+
@annotations_with_whitespace = annotations_with_whitespace
|
22
|
+
@has_leading_whitespace = has_leading_whitespace
|
23
|
+
@has_trailing_whitespace = has_trailing_whitespace
|
24
|
+
@annotation_position = annotation_position
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :annotations, :annotation_position
|
28
|
+
|
29
|
+
# Returns annotations with new line before and after if they exist
|
30
|
+
attr_reader :annotations_with_whitespace
|
31
|
+
|
32
|
+
def annotations_changed?
|
33
|
+
@annotations_changed
|
34
|
+
end
|
35
|
+
|
36
|
+
def has_annotations?
|
37
|
+
@has_annotations
|
38
|
+
end
|
39
|
+
|
40
|
+
def has_skip_string?
|
41
|
+
@has_skip_string
|
42
|
+
end
|
43
|
+
|
44
|
+
def has_leading_whitespace?
|
45
|
+
@has_leading_whitespace
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_trailing_whitespace?
|
49
|
+
@has_trailing_whitespace
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module ModelAnnotator
|
5
|
+
module FileParser
|
6
|
+
autoload :AnnotationFinder, "annotate_rb/model_annotator/file_parser/annotation_finder"
|
7
|
+
autoload :CustomParser, "annotate_rb/model_annotator/file_parser/custom_parser"
|
8
|
+
autoload :ParsedFile, "annotate_rb/model_annotator/file_parser/parsed_file"
|
9
|
+
autoload :ParsedFileResult, "annotate_rb/model_annotator/file_parser/parsed_file_result"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -8,6 +8,13 @@ module AnnotateRb
|
|
8
8
|
# Check for namespaced models in subdirectories as well as models
|
9
9
|
# in subdirectories without namespacing.
|
10
10
|
def call(file, options)
|
11
|
+
use_zeitwerk = defined?(::Rails) && ::Rails.try(:autoloaders).try(:zeitwerk_enabled?)
|
12
|
+
|
13
|
+
if use_zeitwerk
|
14
|
+
klass = ZeitwerkClassGetter.call(file, options)
|
15
|
+
return klass if klass
|
16
|
+
end
|
17
|
+
|
11
18
|
model_path = file.gsub(/\.rb$/, "")
|
12
19
|
options[:model_dir].each { |dir| model_path = model_path.gsub(/^#{dir}/, "").gsub(/^\//, "") }
|
13
20
|
|
@@ -9,13 +9,9 @@ module AnnotateRb
|
|
9
9
|
# of model files from root dir. Otherwise we take all the model files
|
10
10
|
# in the model_dir directory.
|
11
11
|
def call(options)
|
12
|
-
model_files =
|
12
|
+
model_files = list_model_files_from_argument(options)
|
13
13
|
|
14
|
-
|
15
|
-
# It's an artifact from the old Annotate gem and how it did control flow.
|
16
|
-
model_files = list_model_files_from_argument(options) if !options[:is_rake]
|
17
|
-
|
18
|
-
return model_files if !model_files.empty?
|
14
|
+
return model_files if model_files.any?
|
19
15
|
|
20
16
|
options[:model_dir].each do |dir|
|
21
17
|
Dir.chdir(dir) do
|
@@ -41,9 +37,9 @@ module AnnotateRb
|
|
41
37
|
private
|
42
38
|
|
43
39
|
def list_model_files_from_argument(options)
|
44
|
-
return [] if
|
40
|
+
return [] if options.get_state(:working_args).empty?
|
45
41
|
|
46
|
-
specified_files =
|
42
|
+
specified_files = options.get_state(:working_args).map { |file| File.expand_path(file) }
|
47
43
|
|
48
44
|
model_files = options[:model_dir].flat_map do |dir|
|
49
45
|
absolute_dir_path = File.expand_path(dir)
|
@@ -6,7 +6,7 @@ module AnnotateRb
|
|
6
6
|
# Should be the wrapper for an ActiveRecord model that serves as the source of truth of the model
|
7
7
|
# of the model that we're annotating
|
8
8
|
|
9
|
-
def initialize(klass, options
|
9
|
+
def initialize(klass, options)
|
10
10
|
@klass = klass
|
11
11
|
@options = options
|
12
12
|
end
|
@@ -115,7 +115,16 @@ module AnnotateRb
|
|
115
115
|
|
116
116
|
# Try to search the table without prefix
|
117
117
|
table_name_without_prefix = table_name.to_s.sub(@klass.table_name_prefix, "")
|
118
|
-
|
118
|
+
begin
|
119
|
+
@klass.connection.indexes(table_name_without_prefix)
|
120
|
+
rescue ActiveRecord::StatementInvalid => _e
|
121
|
+
# Mysql2 adapter behaves differently than Sqlite3 and Postgres adapter.
|
122
|
+
# If `table_name_without_prefix` does not exist, Mysql2 will raise,
|
123
|
+
# the other adapters will return an empty array.
|
124
|
+
#
|
125
|
+
# See: https://github.com/rails/rails/issues/51205
|
126
|
+
[]
|
127
|
+
end
|
119
128
|
end
|
120
129
|
|
121
130
|
def with_comments?
|
@@ -142,6 +142,8 @@ module AnnotateRb
|
|
142
142
|
File.join(root_directory, FilePatterns::FACTORY_BOT_SPEC_DIR, "%TABLE_NAME%.rb"), # (new style)
|
143
143
|
File.join(root_directory, FilePatterns::FACTORY_BOT_TEST_DIR, "%PLURALIZED_MODEL_NAME%.rb"), # (new style)
|
144
144
|
File.join(root_directory, FilePatterns::FACTORY_BOT_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.rb"), # (new style)
|
145
|
+
File.join(root_directory, FilePatterns::FACTORY_BOT_TEST_DIR, "%PLURALIZED_MODEL_NAME%_factory.rb"), # (new style)
|
146
|
+
File.join(root_directory, FilePatterns::FACTORY_BOT_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_factory.rb"), # (new style)
|
145
147
|
File.join(root_directory, FilePatterns::FABRICATORS_TEST_DIR, "%MODEL_NAME%_fabricator.rb"),
|
146
148
|
File.join(root_directory, FilePatterns::FABRICATORS_SPEC_DIR, "%MODEL_NAME%_fabricator.rb")
|
147
149
|
]
|
@@ -24,10 +24,10 @@ module AnnotateRb
|
|
24
24
|
add_related_scaffold_files if !@options[:exclude_scaffolds]
|
25
25
|
add_related_controller_files if !@options[:exclude_controllers]
|
26
26
|
add_related_helper_files if !@options[:exclude_helpers]
|
27
|
-
add_related_admin_files if
|
28
|
-
add_additional_file_patterns if
|
27
|
+
add_related_admin_files if @options[:active_admin]
|
28
|
+
add_additional_file_patterns if @options[:additional_file_patterns].present?
|
29
29
|
|
30
|
-
@list
|
30
|
+
@list.uniq
|
31
31
|
end
|
32
32
|
|
33
33
|
private
|
@@ -12,20 +12,18 @@ module AnnotateRb
|
|
12
12
|
return false unless File.exist?(file_name)
|
13
13
|
old_content = File.read(file_name)
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
wrapper_open = if options[:wrapper_open]
|
22
|
-
"# #{options[:wrapper_open]}\n"
|
23
|
-
else
|
24
|
-
""
|
15
|
+
begin
|
16
|
+
parsed_file = FileParser::ParsedFile.new(old_content, "", options).parse
|
17
|
+
rescue FileParser::AnnotationFinder::MalformedAnnotation => e
|
18
|
+
warn "Unable to process #{file_name}: #{e.message}"
|
19
|
+
warn "\t" + e.backtrace.join("\n\t") if @options[:trace]
|
20
|
+
return false
|
25
21
|
end
|
26
22
|
|
27
|
-
|
28
|
-
|
23
|
+
return false if !parsed_file.has_annotations?
|
24
|
+
return false if parsed_file.has_skip_string?
|
25
|
+
|
26
|
+
updated_file_content = old_content.sub(parsed_file.annotations_with_whitespace, "")
|
29
27
|
|
30
28
|
File.open(file_name, "wb") { |f| f.puts updated_file_content }
|
31
29
|
|
@@ -21,22 +21,29 @@ module AnnotateRb
|
|
21
21
|
# :position_in_*<Symbol>:: where to place the annotated section in fixture or model file,
|
22
22
|
# :before, :top, :after or :bottom. Default is :before.
|
23
23
|
#
|
24
|
-
def call(file_name, annotation, annotation_position, options
|
24
|
+
def call(file_name, annotation, annotation_position, options)
|
25
25
|
return false unless File.exist?(file_name)
|
26
26
|
old_content = File.read(file_name)
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
begin
|
29
|
+
parsed_file = FileParser::ParsedFile.new(old_content, annotation, options).parse
|
30
|
+
rescue FileParser::AnnotationFinder::MalformedAnnotation => e
|
31
|
+
warn "Unable to process #{file_name}: #{e.message}"
|
32
|
+
warn "\t" + e.backtrace.join("\n\t") if @options[:trace]
|
33
|
+
return false
|
34
|
+
end
|
30
35
|
|
31
|
-
return false if
|
32
|
-
return false if !
|
36
|
+
return false if parsed_file.has_skip_string?
|
37
|
+
return false if !parsed_file.annotations_changed? && !options[:force]
|
33
38
|
|
34
39
|
abort "AnnotateRb error. #{file_name} needs to be updated, but annotaterb was run with `--frozen`." if options[:frozen]
|
35
40
|
|
36
|
-
updated_file_content = if !
|
37
|
-
|
41
|
+
updated_file_content = if !parsed_file.has_annotations?
|
42
|
+
AnnotatedFile::Generator.new(old_content, annotation, annotation_position, options).generate
|
43
|
+
elsif options[:force]
|
44
|
+
AnnotatedFile::Generator.new(old_content, annotation, annotation_position, options).generate
|
38
45
|
else
|
39
|
-
|
46
|
+
AnnotatedFile::Updater.new(old_content, annotation, annotation_position, options).update
|
40
47
|
end
|
41
48
|
|
42
49
|
File.open(file_name, "wb") { |f| f.puts updated_file_content }
|
@@ -4,7 +4,7 @@ module AnnotateRb
|
|
4
4
|
module ModelAnnotator
|
5
5
|
# A plain old Ruby object (PORO) that contains all necessary information for SingleFileAnnotator
|
6
6
|
class SingleFileAnnotatorInstruction
|
7
|
-
def initialize(file, annotation, position, options
|
7
|
+
def initialize(file, annotation, position, options)
|
8
8
|
@file = file # Path to file
|
9
9
|
@annotation = annotation # Annotation string
|
10
10
|
@position = position # Position in the file where to write the annotation to
|
@@ -4,7 +4,7 @@ module AnnotateRb
|
|
4
4
|
module ModelAnnotator
|
5
5
|
# A plain old Ruby object (PORO) that contains all necessary information for SingleFileAnnotationRemover
|
6
6
|
class SingleFileRemoveAnnotationInstruction
|
7
|
-
def initialize(file, options
|
7
|
+
def initialize(file, options)
|
8
8
|
@file = file # Path to file
|
9
9
|
@options = options
|
10
10
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module ModelAnnotator
|
5
|
+
class ZeitwerkClassGetter
|
6
|
+
class << self
|
7
|
+
def call(file, options)
|
8
|
+
new(file, options).call
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(file, options)
|
13
|
+
@file = file
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Constant, nil] Attempts to return the model class constant (e.g. User) defined in the model file
|
18
|
+
# can return `nil` if the file does not define the constant.
|
19
|
+
def call
|
20
|
+
return unless defined?(::Zeitwerk)
|
21
|
+
|
22
|
+
@absolute_file_path = File.expand_path(@file)
|
23
|
+
loader = ::Rails.autoloaders.main
|
24
|
+
|
25
|
+
if supports_cpath?
|
26
|
+
constant_using_cpath(loader)
|
27
|
+
else
|
28
|
+
constant(loader)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def constant(loader)
|
35
|
+
root_dirs = loader.dirs(namespaces: true) # or `root_dirs = loader.root_dirs` with zeitwerk < 2.6.1
|
36
|
+
expanded_file = @absolute_file_path
|
37
|
+
|
38
|
+
# root_dir: "/home/dummyapp/app/models"
|
39
|
+
root_dir, namespace = root_dirs.find do |dir, _namespace|
|
40
|
+
expanded_file.start_with?(dir)
|
41
|
+
end
|
42
|
+
|
43
|
+
# expanded_file: "/home/dummyapp/app/models/collapsed/example/test_model.rb"
|
44
|
+
# filepath_relative_to_root_dir: "/collapsed/example/test_model.rb"
|
45
|
+
_, filepath_relative_to_root_dir = expanded_file.split(root_dir)
|
46
|
+
|
47
|
+
# Remove leading / and the .rb extension
|
48
|
+
filepath_relative_to_root_dir = filepath_relative_to_root_dir[1..].sub(/\.rb$/, "")
|
49
|
+
|
50
|
+
# once we have the filepath_relative_to_root_dir, we need to see if it
|
51
|
+
# falls within one of our Zeitwerk "collapsed" paths.
|
52
|
+
if loader.collapse.any? { |path| path.include?(root_dir) && file.include?(path.split(root_dir)[1]) }
|
53
|
+
# if the file is within a collapsed path, we then need to, for each
|
54
|
+
# collapsed path, remove the root dir
|
55
|
+
collapsed = loader.collapse.map { |path| path.split(root_dir)[1].sub(/^\//, "") }.to_set
|
56
|
+
|
57
|
+
collapsed.each do |collapse|
|
58
|
+
# next, we split the collapsed directory, e.g. `domain_name/models`, by
|
59
|
+
# slash, and discard the domain_name
|
60
|
+
_, *collapsed_namespace = collapse.split("/")
|
61
|
+
|
62
|
+
# if there are any collapsed namespaces, e.g. `models`, we then remove
|
63
|
+
# that from `filepath_relative_to_root_dir`.
|
64
|
+
#
|
65
|
+
# This would result in:
|
66
|
+
#
|
67
|
+
# previous filepath_relative_to_root_dir: domain_name/models/model_name
|
68
|
+
# new filepath_relative_to_root_dir: domain_name/model_name
|
69
|
+
if collapsed_namespace.any?
|
70
|
+
filepath_relative_to_root_dir.sub!("/#{collapsed_namespace.last}", "")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
camelize = loader.inflector.camelize(filepath_relative_to_root_dir, nil)
|
76
|
+
namespace.const_get(camelize)
|
77
|
+
rescue NameError => e
|
78
|
+
warn e
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def constant_using_cpath(loader)
|
83
|
+
begin
|
84
|
+
constant = loader.cpath_expected_at(@absolute_file_path)
|
85
|
+
rescue ::Zeitwerk::Error => e
|
86
|
+
# Raises when file does not exist
|
87
|
+
warn "Zeitwerk unable to find file #{@file}, error:\n#{e.message}"
|
88
|
+
return
|
89
|
+
end
|
90
|
+
|
91
|
+
begin
|
92
|
+
# This uses ActiveSupport::Inflector.constantize
|
93
|
+
klass = constant.constantize
|
94
|
+
rescue NameError => e
|
95
|
+
warn e
|
96
|
+
return
|
97
|
+
end
|
98
|
+
|
99
|
+
klass
|
100
|
+
end
|
101
|
+
|
102
|
+
def supports_cpath?
|
103
|
+
@supports_cpath ||=
|
104
|
+
begin
|
105
|
+
current_version = ::Gem::Version.new(::Zeitwerk::VERSION)
|
106
|
+
required_version = ::Gem::Version.new("2.6.9")
|
107
|
+
|
108
|
+
current_version >= required_version
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -7,7 +7,6 @@ module AnnotateRb
|
|
7
7
|
autoload :BadModelFileError, "annotate_rb/model_annotator/bad_model_file_error"
|
8
8
|
autoload :FileNameResolver, "annotate_rb/model_annotator/file_name_resolver"
|
9
9
|
autoload :SingleFileAnnotationRemover, "annotate_rb/model_annotator/single_file_annotation_remover"
|
10
|
-
autoload :AnnotationPatternGenerator, "annotate_rb/model_annotator/annotation_pattern_generator"
|
11
10
|
autoload :ModelClassGetter, "annotate_rb/model_annotator/model_class_getter"
|
12
11
|
autoload :ModelFilesGetter, "annotate_rb/model_annotator/model_files_getter"
|
13
12
|
autoload :SingleFileAnnotator, "annotate_rb/model_annotator/single_file_annotator"
|
@@ -22,10 +21,10 @@ module AnnotateRb
|
|
22
21
|
autoload :SingleFileRemoveAnnotationInstruction, "annotate_rb/model_annotator/single_file_remove_annotation_instruction"
|
23
22
|
autoload :AnnotationDiffGenerator, "annotate_rb/model_annotator/annotation_diff_generator"
|
24
23
|
autoload :AnnotationDiff, "annotate_rb/model_annotator/annotation_diff"
|
25
|
-
autoload :FileBuilder, "annotate_rb/model_annotator/file_builder"
|
26
|
-
autoload :MagicCommentParser, "annotate_rb/model_annotator/magic_comment_parser"
|
27
|
-
autoload :FileComponents, "annotate_rb/model_annotator/file_components"
|
28
24
|
autoload :ProjectAnnotator, "annotate_rb/model_annotator/project_annotator"
|
29
25
|
autoload :ProjectAnnotationRemover, "annotate_rb/model_annotator/project_annotation_remover"
|
26
|
+
autoload :AnnotatedFile, "annotate_rb/model_annotator/annotated_file"
|
27
|
+
autoload :FileParser, "annotate_rb/model_annotator/file_parser"
|
28
|
+
autoload :ZeitwerkClassGetter, "annotate_rb/model_annotator/zeitwerk_class_getter"
|
30
29
|
end
|
31
30
|
end
|
data/lib/annotate_rb/options.rb
CHANGED
data/lib/annotate_rb/parser.rb
CHANGED
@@ -3,7 +3,7 @@ require "optparse"
|
|
3
3
|
module AnnotateRb
|
4
4
|
# Class for handling command line arguments
|
5
5
|
class Parser # rubocop:disable Metrics/ClassLength
|
6
|
-
def self.parse(args, existing_options
|
6
|
+
def self.parse(args, existing_options)
|
7
7
|
new(args, existing_options).parse
|
8
8
|
end
|
9
9
|
|
@@ -35,11 +35,11 @@ module AnnotateRb
|
|
35
35
|
}.freeze
|
36
36
|
|
37
37
|
def initialize(args, existing_options)
|
38
|
-
@args = args
|
38
|
+
@args = args.clone
|
39
39
|
base_options = DEFAULT_OPTIONS.dup
|
40
40
|
@options = base_options.merge(existing_options)
|
41
41
|
@commands = []
|
42
|
-
@options[:original_args] = args.
|
42
|
+
@options[:original_args] = args.clone
|
43
43
|
end
|
44
44
|
|
45
45
|
def parse
|
@@ -52,6 +52,12 @@ module AnnotateRb
|
|
52
52
|
@options
|
53
53
|
end
|
54
54
|
|
55
|
+
def remaining_args
|
56
|
+
# `@args` gets modified throughout the lifecycle of this class.
|
57
|
+
# It starts as a shallow clone of ARGV, then arguments matching commands and options are removed in #parse
|
58
|
+
@args
|
59
|
+
end
|
60
|
+
|
55
61
|
private
|
56
62
|
|
57
63
|
def parse_command(args)
|
data/lib/annotate_rb/runner.rb
CHANGED
@@ -9,14 +9,15 @@ module AnnotateRb
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def run(args)
|
12
|
-
_original_args = args.dup
|
13
|
-
|
14
12
|
config_file_options = ConfigLoader.load_config
|
15
|
-
|
13
|
+
parser = Parser.new(args, {})
|
14
|
+
|
15
|
+
parsed_options = parser.parse
|
16
|
+
remaining_args = parser.remaining_args
|
16
17
|
|
17
18
|
options = config_file_options.merge(parsed_options)
|
18
19
|
|
19
|
-
@options = Options.from(options, {})
|
20
|
+
@options = Options.from(options, {working_args: remaining_args})
|
20
21
|
AnnotateRb::RakeBootstrapper.call(@options)
|
21
22
|
|
22
23
|
if @options[:command]
|
@@ -6,6 +6,11 @@
|
|
6
6
|
|
7
7
|
# Migration tasks are tasks that we'll "hook" into
|
8
8
|
migration_tasks = %w[db:migrate db:migrate:up db:migrate:down db:migrate:reset db:migrate:redo db:rollback]
|
9
|
+
|
10
|
+
# Support for data_migrate gem (https://github.com/ilyakatz/data-migrate)
|
11
|
+
migration_tasks_with_data = migration_tasks.map { |task| "#{task}:with_data" }
|
12
|
+
migration_tasks += migration_tasks_with_data
|
13
|
+
|
9
14
|
if defined?(Rails::Application) && Rails.version.split(".").first.to_i >= 6
|
10
15
|
require "active_record"
|
11
16
|
|
data/lib/annotate_rb.rb
CHANGED
@@ -22,6 +22,7 @@ require_relative "annotate_rb/eager_loader"
|
|
22
22
|
require_relative "annotate_rb/rake_bootstrapper"
|
23
23
|
require_relative "annotate_rb/config_finder"
|
24
24
|
require_relative "annotate_rb/config_loader"
|
25
|
+
require_relative "annotate_rb/config_generator"
|
25
26
|
|
26
27
|
module AnnotateRb
|
27
28
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "annotate_rb"
|
4
|
+
|
5
|
+
module AnnotateRb
|
6
|
+
module Generators
|
7
|
+
class ConfigGenerator < ::Rails::Generators::Base
|
8
|
+
def generate_config
|
9
|
+
create_file ::AnnotateRb::ConfigFinder::DOTFILE do
|
10
|
+
::AnnotateRb::ConfigGenerator.default_config_yml
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "annotate_rb"
|
4
|
+
|
5
|
+
module AnnotateRb
|
6
|
+
module Generators
|
7
|
+
class HookGenerator < ::Rails::Generators::Base
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
9
|
+
|
10
|
+
def copy_hook_file
|
11
|
+
copy_file "annotate_rb.rake", "lib/tasks/annotate_rb.rake"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -5,10 +5,9 @@ require "annotate_rb"
|
|
5
5
|
module AnnotateRb
|
6
6
|
module Generators
|
7
7
|
class InstallGenerator < ::Rails::Generators::Base
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
copy_file "annotate_rb.rake", "lib/tasks/annotate_rb.rake"
|
8
|
+
def install_hook_and_generate_defaults
|
9
|
+
generate "annotate_rb:hook"
|
10
|
+
generate "annotate_rb:config"
|
12
11
|
end
|
13
12
|
end
|
14
13
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "annotate_rb"
|
4
|
+
|
5
|
+
module AnnotateRb
|
6
|
+
module Generators
|
7
|
+
class UpdateConfigGenerator < ::Rails::Generators::Base
|
8
|
+
def generate_config
|
9
|
+
insert_into_file ::AnnotateRb::ConfigFinder::DOTFILE do
|
10
|
+
::AnnotateRb::ConfigGenerator.unset_config_defaults
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|