annotaterb 4.1.0 → 4.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -0
- data/README.md +4 -5
- data/VERSION +1 -1
- data/exe/annotaterb +7 -7
- data/lib/annotate_rb/active_record_patch.rb +2 -0
- data/lib/annotate_rb/commands/annotate_models.rb +0 -1
- data/lib/annotate_rb/commands/annotate_routes.rb +0 -1
- data/lib/annotate_rb/commands/print_help.rb +0 -1
- data/lib/annotate_rb/commands/print_version.rb +0 -1
- data/lib/annotate_rb/commands.rb +4 -4
- data/lib/annotate_rb/config_finder.rb +1 -1
- data/lib/annotate_rb/config_loader.rb +2 -2
- data/lib/annotate_rb/core.rb +3 -3
- data/lib/annotate_rb/helper.rb +16 -0
- data/lib/annotate_rb/model_annotator/{annotation_generator.rb → annotation_builder.rb} +18 -14
- data/lib/annotate_rb/model_annotator/annotation_decider.rb +10 -12
- data/lib/annotate_rb/model_annotator/annotation_diff.rb +19 -0
- data/lib/annotate_rb/model_annotator/annotation_diff_generator.rb +44 -0
- data/lib/annotate_rb/model_annotator/annotation_pattern_generator.rb +3 -3
- data/lib/annotate_rb/model_annotator/annotator.rb +12 -53
- data/lib/annotate_rb/model_annotator/column_annotation/annotation_builder.rb +135 -0
- data/lib/annotate_rb/model_annotator/column_annotation/attributes_builder.rb +104 -0
- data/lib/annotate_rb/model_annotator/column_annotation/column_wrapper.rb +103 -0
- data/lib/annotate_rb/model_annotator/column_annotation/type_builder.rb +54 -0
- data/lib/annotate_rb/model_annotator/column_annotation.rb +12 -0
- data/lib/annotate_rb/model_annotator/file_builder.rb +58 -0
- data/lib/annotate_rb/model_annotator/file_components.rb +78 -0
- data/lib/annotate_rb/model_annotator/file_name_resolver.rb +3 -3
- data/lib/annotate_rb/model_annotator/foreign_key_annotation/annotation_builder.rb +57 -0
- data/lib/annotate_rb/model_annotator/foreign_key_annotation.rb +9 -0
- data/lib/annotate_rb/model_annotator/index_annotation/annotation_builder.rb +113 -0
- data/lib/annotate_rb/model_annotator/index_annotation.rb +9 -0
- data/lib/annotate_rb/model_annotator/magic_comment_parser.rb +32 -0
- data/lib/annotate_rb/model_annotator/model_class_getter.rb +6 -6
- data/lib/annotate_rb/model_annotator/model_files_getter.rb +12 -10
- data/lib/annotate_rb/model_annotator/model_wrapper.rb +44 -37
- data/lib/annotate_rb/model_annotator/pattern_getter.rb +142 -10
- data/lib/annotate_rb/model_annotator/project_annotation_remover.rb +65 -0
- data/lib/annotate_rb/model_annotator/project_annotator.rb +63 -0
- data/lib/annotate_rb/model_annotator/related_files_list_builder.rb +16 -18
- data/lib/annotate_rb/model_annotator/single_file_annotation_remover.rb +37 -0
- data/lib/annotate_rb/model_annotator/single_file_annotator.rb +49 -0
- data/lib/annotate_rb/model_annotator/{file_annotator_instruction.rb → single_file_annotator_instruction.rb} +2 -2
- data/lib/annotate_rb/model_annotator/single_file_remove_annotation_instruction.rb +15 -0
- data/lib/annotate_rb/model_annotator.rb +25 -24
- data/lib/annotate_rb/options.rb +20 -25
- data/lib/annotate_rb/parser.rb +150 -142
- data/lib/annotate_rb/rake_bootstrapper.rb +8 -8
- data/lib/annotate_rb/route_annotator/annotation_processor.rb +7 -8
- data/lib/annotate_rb/route_annotator/annotator.rb +2 -2
- data/lib/annotate_rb/route_annotator/base_processor.rb +3 -3
- data/lib/annotate_rb/route_annotator/header_generator.rb +15 -15
- data/lib/annotate_rb/route_annotator/helper.rb +9 -9
- data/lib/annotate_rb/route_annotator/removal_processor.rb +4 -4
- data/lib/annotate_rb/route_annotator.rb +6 -6
- data/lib/annotate_rb/runner.rb +0 -4
- data/lib/annotate_rb/tasks/annotate_models_migrate.rake +5 -5
- data/lib/annotate_rb.rb +19 -19
- data/lib/generators/annotate_rb/install/install_generator.rb +3 -2
- data/lib/generators/annotate_rb/install/templates/annotate_rb.rake +1 -1
- metadata +24 -16
- data/lib/annotate_rb/model_annotator/column_annotation_builder.rb +0 -92
- data/lib/annotate_rb/model_annotator/column_attributes_builder.rb +0 -102
- data/lib/annotate_rb/model_annotator/column_type_builder.rb +0 -51
- data/lib/annotate_rb/model_annotator/column_wrapper.rb +0 -84
- data/lib/annotate_rb/model_annotator/constants.rb +0 -22
- data/lib/annotate_rb/model_annotator/file_annotation_remover.rb +0 -25
- data/lib/annotate_rb/model_annotator/file_annotator.rb +0 -83
- data/lib/annotate_rb/model_annotator/file_patterns.rb +0 -129
- data/lib/annotate_rb/model_annotator/foreign_key_annotation_builder.rb +0 -55
- data/lib/annotate_rb/model_annotator/helper.rb +0 -107
- data/lib/annotate_rb/model_annotator/index_annotation_builder.rb +0 -74
- data/lib/annotate_rb/model_annotator/model_file_annotator.rb +0 -55
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AnnotateRb
|
|
4
|
+
module ModelAnnotator
|
|
5
|
+
module IndexAnnotation
|
|
6
|
+
class AnnotationBuilder
|
|
7
|
+
INDEX_CLAUSES = {
|
|
8
|
+
unique: {
|
|
9
|
+
default: "UNIQUE",
|
|
10
|
+
markdown: "_unique_"
|
|
11
|
+
},
|
|
12
|
+
where: {
|
|
13
|
+
default: "WHERE",
|
|
14
|
+
markdown: "_where_"
|
|
15
|
+
},
|
|
16
|
+
using: {
|
|
17
|
+
default: "USING",
|
|
18
|
+
markdown: "_using_"
|
|
19
|
+
}
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
def initialize(model, options)
|
|
23
|
+
@model = model
|
|
24
|
+
@options = options
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def build
|
|
28
|
+
index_info = if @options[:format_markdown]
|
|
29
|
+
"#\n# ### Indexes\n#\n"
|
|
30
|
+
else
|
|
31
|
+
"#\n# Indexes\n#\n"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
indexes = @model.retrieve_indexes_from_table
|
|
35
|
+
return "" if indexes.empty?
|
|
36
|
+
|
|
37
|
+
max_size = indexes.collect { |index| index.name.size }.max + 1
|
|
38
|
+
indexes.sort_by(&:name).each do |index|
|
|
39
|
+
index_info += if @options[:format_markdown]
|
|
40
|
+
final_index_string_in_markdown(index)
|
|
41
|
+
else
|
|
42
|
+
final_index_string(index, max_size)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
index_info
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def index_using_info(index, format = :default)
|
|
52
|
+
value = index.try(:using) && index.using.try(:to_sym)
|
|
53
|
+
if !value.blank? && value != :btree
|
|
54
|
+
" #{INDEX_CLAUSES[:using][format]} #{value}"
|
|
55
|
+
else
|
|
56
|
+
""
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def index_where_info(index, format = :default)
|
|
61
|
+
value = index.try(:where).try(:to_s)
|
|
62
|
+
if value.blank?
|
|
63
|
+
""
|
|
64
|
+
else
|
|
65
|
+
" #{INDEX_CLAUSES[:where][format]} #{value}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def index_unique_info(index, format = :default)
|
|
70
|
+
index.unique ? " #{INDEX_CLAUSES[:unique][format]}" : ""
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def final_index_string_in_markdown(index)
|
|
74
|
+
details = format(
|
|
75
|
+
"%s%s%s",
|
|
76
|
+
index_unique_info(index, :markdown),
|
|
77
|
+
index_where_info(index, :markdown),
|
|
78
|
+
index_using_info(index, :markdown)
|
|
79
|
+
).strip
|
|
80
|
+
details = " (#{details})" unless details.blank?
|
|
81
|
+
|
|
82
|
+
format(
|
|
83
|
+
"# * `%s`%s:\n# * **`%s`**\n",
|
|
84
|
+
index.name,
|
|
85
|
+
details,
|
|
86
|
+
index_columns_info(index).join("`**\n# * **`")
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def final_index_string(index, max_size)
|
|
91
|
+
format(
|
|
92
|
+
"# %-#{max_size}.#{max_size}s %s%s%s%s",
|
|
93
|
+
index.name,
|
|
94
|
+
"(#{index_columns_info(index).join(",")})",
|
|
95
|
+
index_unique_info(index),
|
|
96
|
+
index_where_info(index),
|
|
97
|
+
index_using_info(index)
|
|
98
|
+
).rstrip + "\n"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def index_columns_info(index)
|
|
102
|
+
Array(index.columns).map do |col|
|
|
103
|
+
if index.try(:orders) && index.orders[col.to_s]
|
|
104
|
+
"#{col} #{index.orders[col.to_s].upcase}"
|
|
105
|
+
else
|
|
106
|
+
col.to_s.gsub("\r", '\r').gsub("\n", '\n')
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AnnotateRb
|
|
4
|
+
module ModelAnnotator
|
|
5
|
+
# Extracts magic comments strings and returns them
|
|
6
|
+
class MagicCommentParser
|
|
7
|
+
MAGIC_COMMENTS = [
|
|
8
|
+
HASH_ENCODING = /(^#\s*encoding:.*(?:\n|r\n))/,
|
|
9
|
+
HASH_CODING = /(^# coding:.*(?:\n|\r\n))/,
|
|
10
|
+
HASH_FROZEN_STRING = /(^#\s*frozen_string_literal:.+(?:\n|\r\n))/,
|
|
11
|
+
STAR_ENCODING = /(^# -\*- encoding\s?:.*(?:\n|\r\n))/,
|
|
12
|
+
STAR_CODING = /(^# -\*- coding:.*(?:\n|\r\n))/,
|
|
13
|
+
STAR_FROZEN_STRING = /(^# -\*- frozen_string_literal\s*:.+-\*-(?:\n|\r\n))/,
|
|
14
|
+
SORBET_TYPED_STRING = /(^#\s*typed:.*(?:\n|r\n))/.freeze
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
MAGIC_COMMENTS_REGEX = Regexp.union(*MAGIC_COMMENTS).freeze
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def call(content)
|
|
21
|
+
magic_comments = content.scan(MAGIC_COMMENTS_REGEX).flatten.compact
|
|
22
|
+
|
|
23
|
+
if magic_comments.any?
|
|
24
|
+
magic_comments.join
|
|
25
|
+
else
|
|
26
|
+
""
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -8,8 +8,8 @@ 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
|
-
model_path = file.gsub(/\.rb$/,
|
|
12
|
-
options[:model_dir].each { |dir| model_path = model_path.gsub(/^#{dir}/,
|
|
11
|
+
model_path = file.gsub(/\.rb$/, "")
|
|
12
|
+
options[:model_dir].each { |dir| model_path = model_path.gsub(/^#{dir}/, "").gsub(/^\//, "") }
|
|
13
13
|
|
|
14
14
|
begin
|
|
15
15
|
get_loaded_model(model_path, file) || raise(BadModelFileError.new)
|
|
@@ -18,8 +18,8 @@ module AnnotateRb
|
|
|
18
18
|
file_path = File.expand_path(file)
|
|
19
19
|
if File.file?(file_path) && Kernel.require(file_path)
|
|
20
20
|
retry
|
|
21
|
-
elsif model_path
|
|
22
|
-
model_path = model_path.split(
|
|
21
|
+
elsif /\//.match?(model_path)
|
|
22
|
+
model_path = model_path.split("/")[1..-1].join("/").to_s
|
|
23
23
|
retry
|
|
24
24
|
else
|
|
25
25
|
raise
|
|
@@ -39,7 +39,7 @@ module AnnotateRb
|
|
|
39
39
|
absolute_file = File.expand_path(file)
|
|
40
40
|
model_paths =
|
|
41
41
|
$LOAD_PATH.select { |path| absolute_file.include?(path) }
|
|
42
|
-
|
|
42
|
+
.map { |path| absolute_file.sub(path, "").sub(/\.rb$/, "").sub(/^\//, "") }
|
|
43
43
|
model_paths
|
|
44
44
|
.map { |path| get_loaded_model_by_path(path) }
|
|
45
45
|
.find { |loaded_model| !loaded_model.nil? }
|
|
@@ -51,7 +51,7 @@ module AnnotateRb
|
|
|
51
51
|
rescue StandardError, LoadError
|
|
52
52
|
# Revert to the old way but it is not really robust
|
|
53
53
|
ObjectSpace.each_object(::Class)
|
|
54
|
-
|
|
54
|
+
.select do |c|
|
|
55
55
|
Class === c && # note: we use === to avoid a bug in activesupport 2.3.14 OptionMerger vs. is_a?
|
|
56
56
|
c.ancestors.respond_to?(:include?) && # to fix FactoryGirl bug, see https://github.com/ctran/annotate_models/pull/82
|
|
57
57
|
c.ancestors.include?(::ActiveRecord::Base)
|
|
@@ -20,19 +20,21 @@ module AnnotateRb
|
|
|
20
20
|
options[:model_dir].each do |dir|
|
|
21
21
|
Dir.chdir(dir) do
|
|
22
22
|
list = if options[:ignore_model_sub_dir]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
Dir["*.rb"].map { |f| [dir, f] }
|
|
24
|
+
else
|
|
25
|
+
Dir["**/*.rb"]
|
|
26
|
+
.reject { |f| f["concerns/"] }
|
|
27
|
+
.map { |f| [dir, f] }
|
|
28
|
+
end
|
|
27
29
|
model_files.concat(list)
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
model_files
|
|
32
34
|
rescue SystemCallError
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
warn "No models found in directory '#{options[:model_dir].join("', '")}'."
|
|
36
|
+
warn "Either specify models on the command line, or use the --model-dir option."
|
|
37
|
+
warn "Call 'annotaterb --help' for more info."
|
|
36
38
|
# exit 1 # TODO: Return exit code back to caller. Right now it messes up RSpec being able to run
|
|
37
39
|
end
|
|
38
40
|
|
|
@@ -47,12 +49,12 @@ module AnnotateRb
|
|
|
47
49
|
absolute_dir_path = File.expand_path(dir)
|
|
48
50
|
specified_files
|
|
49
51
|
.find_all { |file| file.start_with?(absolute_dir_path) }
|
|
50
|
-
.map { |file| [dir, file.sub("#{absolute_dir_path}/",
|
|
52
|
+
.map { |file| [dir, file.sub("#{absolute_dir_path}/", "")] }
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
if model_files.size != specified_files.size
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
warn "The specified file could not be found in directory '#{options[:model_dir].join("', '")}'."
|
|
57
|
+
warn "Call 'annotaterb --help' for more info."
|
|
56
58
|
# exit 1 # TODO: Return exit code back to caller. Right now it messes up RSpec being able to run
|
|
57
59
|
end
|
|
58
60
|
|
|
@@ -13,22 +13,23 @@ module AnnotateRb
|
|
|
13
13
|
|
|
14
14
|
# Gets the columns of the ActiveRecord model, processes them, and then returns them.
|
|
15
15
|
def columns
|
|
16
|
-
@columns ||=
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
@columns ||=
|
|
17
|
+
begin
|
|
18
|
+
cols = raw_columns
|
|
19
|
+
cols += translated_columns
|
|
20
|
+
|
|
21
|
+
ignore_columns = @options[:ignore_columns]
|
|
22
|
+
if ignore_columns
|
|
23
|
+
cols = cols.reject do |col|
|
|
24
|
+
col.name.match(/#{ignore_columns}/)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
cols = cols.sort_by(&:name) if @options[:sort]
|
|
29
|
+
cols = classified_sort(cols) if @options[:classified_sort]
|
|
30
|
+
|
|
31
|
+
cols
|
|
32
|
+
end
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def connection
|
|
@@ -75,19 +76,25 @@ module AnnotateRb
|
|
|
75
76
|
# Calculates the max width of the schema for the model by looking at the columns, schema comments, with respect
|
|
76
77
|
# to the options.
|
|
77
78
|
def max_schema_info_width
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
max_size = cols.map(&:name).map(&:size).max
|
|
87
|
-
end
|
|
88
|
-
max_size += @options[:format_rdoc] ? 5 : 1
|
|
79
|
+
@max_schema_info_width ||=
|
|
80
|
+
begin
|
|
81
|
+
cols = columns
|
|
82
|
+
|
|
83
|
+
if with_comments?
|
|
84
|
+
column_widths = cols.map do |column|
|
|
85
|
+
column.name.size + (column.comment ? Helper.width(column.comment) : 0)
|
|
86
|
+
end
|
|
89
87
|
|
|
90
|
-
|
|
88
|
+
max_size = column_widths.max || 0
|
|
89
|
+
max_size += 2
|
|
90
|
+
else
|
|
91
|
+
max_size = cols.map(&:name).map(&:size).max
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
max_size += @options[:format_rdoc] ? 5 : 1
|
|
95
|
+
|
|
96
|
+
max_size
|
|
97
|
+
end
|
|
91
98
|
end
|
|
92
99
|
|
|
93
100
|
def retrieve_indexes_from_table
|
|
@@ -98,12 +105,12 @@ module AnnotateRb
|
|
|
98
105
|
return indexes if indexes.any? || !@klass.table_name_prefix
|
|
99
106
|
|
|
100
107
|
# Try to search the table without prefix
|
|
101
|
-
table_name_without_prefix = table_name.to_s.sub(@klass.table_name_prefix,
|
|
108
|
+
table_name_without_prefix = table_name.to_s.sub(@klass.table_name_prefix, "")
|
|
102
109
|
@klass.connection.indexes(table_name_without_prefix)
|
|
103
110
|
end
|
|
104
111
|
|
|
105
112
|
def with_comments?
|
|
106
|
-
@options[:with_comment] &&
|
|
113
|
+
@with_comments ||= @options[:with_comment] &&
|
|
107
114
|
raw_columns.first.respond_to?(:comment) &&
|
|
108
115
|
raw_columns.map(&:comment).any? { |comment| !comment.nil? }
|
|
109
116
|
end
|
|
@@ -115,11 +122,11 @@ module AnnotateRb
|
|
|
115
122
|
id = nil
|
|
116
123
|
|
|
117
124
|
cols.each do |c|
|
|
118
|
-
if c.name.eql?(
|
|
125
|
+
if c.name.eql?("id")
|
|
119
126
|
id = c
|
|
120
|
-
elsif c.name.eql?(
|
|
127
|
+
elsif c.name.eql?("created_at") || c.name.eql?("updated_at")
|
|
121
128
|
timestamps << c
|
|
122
|
-
elsif c.name[-3, 3].eql?(
|
|
129
|
+
elsif c.name[-3, 3].eql?("_id")
|
|
123
130
|
associations << c
|
|
124
131
|
else
|
|
125
132
|
rest_cols << c
|
|
@@ -137,9 +144,9 @@ module AnnotateRb
|
|
|
137
144
|
# eg. Model: Car, foreign column name: car_id
|
|
138
145
|
foreign_column_name = [
|
|
139
146
|
@klass.translation_class.to_s
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
.gsub("::Translation", "").gsub("::", "_")
|
|
148
|
+
.downcase,
|
|
149
|
+
"_id"
|
|
143
150
|
].join.to_sym
|
|
144
151
|
|
|
145
152
|
[
|
|
@@ -152,4 +159,4 @@ module AnnotateRb
|
|
|
152
159
|
end
|
|
153
160
|
end
|
|
154
161
|
end
|
|
155
|
-
end
|
|
162
|
+
end
|
|
@@ -3,25 +3,157 @@
|
|
|
3
3
|
module AnnotateRb
|
|
4
4
|
module ModelAnnotator
|
|
5
5
|
class PatternGetter
|
|
6
|
+
module FilePatterns
|
|
7
|
+
# Controller files
|
|
8
|
+
CONTROLLER_DIR = File.join("app", "controllers")
|
|
9
|
+
|
|
10
|
+
# Active admin registry files
|
|
11
|
+
ACTIVEADMIN_DIR = File.join("app", "admin")
|
|
12
|
+
|
|
13
|
+
# Helper files
|
|
14
|
+
HELPER_DIR = File.join("app", "helpers")
|
|
15
|
+
|
|
16
|
+
# File.join for windows reverse bar compat?
|
|
17
|
+
# I dont use windows, can`t test
|
|
18
|
+
UNIT_TEST_DIR = File.join("test", "unit")
|
|
19
|
+
MODEL_TEST_DIR = File.join("test", "models") # since rails 4.0
|
|
20
|
+
SPEC_MODEL_DIR = File.join("spec", "models")
|
|
21
|
+
|
|
22
|
+
FIXTURE_TEST_DIR = File.join("test", "fixtures")
|
|
23
|
+
FIXTURE_SPEC_DIR = File.join("spec", "fixtures")
|
|
24
|
+
|
|
25
|
+
# Other test files
|
|
26
|
+
CONTROLLER_TEST_DIR = File.join("test", "controllers")
|
|
27
|
+
CONTROLLER_SPEC_DIR = File.join("spec", "controllers")
|
|
28
|
+
REQUEST_SPEC_DIR = File.join("spec", "requests")
|
|
29
|
+
ROUTING_SPEC_DIR = File.join("spec", "routing")
|
|
30
|
+
|
|
31
|
+
# Object Daddy http://github.com/flogic/object_daddy/tree/master
|
|
32
|
+
EXEMPLARS_TEST_DIR = File.join("test", "exemplars")
|
|
33
|
+
EXEMPLARS_SPEC_DIR = File.join("spec", "exemplars")
|
|
34
|
+
|
|
35
|
+
# Machinist http://github.com/notahat/machinist
|
|
36
|
+
BLUEPRINTS_TEST_DIR = File.join("test", "blueprints")
|
|
37
|
+
BLUEPRINTS_SPEC_DIR = File.join("spec", "blueprints")
|
|
38
|
+
|
|
39
|
+
# Factory Bot https://github.com/thoughtbot/factory_bot
|
|
40
|
+
FACTORY_BOT_TEST_DIR = File.join("test", "factories")
|
|
41
|
+
FACTORY_BOT_SPEC_DIR = File.join("spec", "factories")
|
|
42
|
+
|
|
43
|
+
# Fabrication https://github.com/paulelliott/fabrication.git
|
|
44
|
+
FABRICATORS_TEST_DIR = File.join("test", "fabricators")
|
|
45
|
+
FABRICATORS_SPEC_DIR = File.join("spec", "fabricators")
|
|
46
|
+
|
|
47
|
+
# Serializers https://github.com/rails-api/active_model_serializers
|
|
48
|
+
SERIALIZERS_DIR = File.join("app", "serializers")
|
|
49
|
+
SERIALIZERS_TEST_DIR = File.join("test", "serializers")
|
|
50
|
+
SERIALIZERS_SPEC_DIR = File.join("spec", "serializers")
|
|
51
|
+
end
|
|
52
|
+
|
|
6
53
|
class << self
|
|
7
54
|
def call(options, pattern_types = [])
|
|
8
|
-
|
|
55
|
+
new(options, pattern_types).get
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def initialize(options, pattern_types = [])
|
|
60
|
+
@options = options
|
|
61
|
+
@pattern_types = pattern_types
|
|
62
|
+
end
|
|
9
63
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
patterns = FilePatterns.generate(root_directory, pattern_type, options)
|
|
64
|
+
def get
|
|
65
|
+
current_patterns = []
|
|
13
66
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
67
|
+
@options[:root_dir].each do |root_directory|
|
|
68
|
+
Array(@pattern_types).each do |pattern_type|
|
|
69
|
+
patterns = generate(root_directory, pattern_type)
|
|
70
|
+
|
|
71
|
+
current_patterns += if pattern_type.to_sym == :additional_file_patterns
|
|
72
|
+
patterns
|
|
73
|
+
else
|
|
74
|
+
patterns.map { |p| p.sub(/^\/*/, "") }
|
|
19
75
|
end
|
|
20
76
|
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
current_patterns
|
|
80
|
+
end
|
|
21
81
|
|
|
22
|
-
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def generate(root_directory, pattern_type)
|
|
85
|
+
case pattern_type
|
|
86
|
+
when "test" then test_files(root_directory)
|
|
87
|
+
when "fixture" then fixture_files(root_directory)
|
|
88
|
+
when "scaffold" then scaffold_files(root_directory)
|
|
89
|
+
when "factory" then factory_files(root_directory)
|
|
90
|
+
when "serializer" then serialize_files(root_directory)
|
|
91
|
+
when "additional_file_patterns"
|
|
92
|
+
[@options[:additional_file_patterns] || []].flatten
|
|
93
|
+
when "controller"
|
|
94
|
+
[File.join(root_directory, FilePatterns::CONTROLLER_DIR, "%PLURALIZED_MODEL_NAME%_controller.rb")]
|
|
95
|
+
when "admin"
|
|
96
|
+
[
|
|
97
|
+
File.join(root_directory, FilePatterns::ACTIVEADMIN_DIR, "%MODEL_NAME%.rb"),
|
|
98
|
+
File.join(root_directory, FilePatterns::ACTIVEADMIN_DIR, "%PLURALIZED_MODEL_NAME%.rb")
|
|
99
|
+
]
|
|
100
|
+
when "helper"
|
|
101
|
+
[File.join(root_directory, FilePatterns::HELPER_DIR, "%PLURALIZED_MODEL_NAME%_helper.rb")]
|
|
102
|
+
else
|
|
103
|
+
[]
|
|
23
104
|
end
|
|
24
105
|
end
|
|
106
|
+
|
|
107
|
+
def test_files(root_directory)
|
|
108
|
+
[
|
|
109
|
+
File.join(root_directory, FilePatterns::UNIT_TEST_DIR, "%MODEL_NAME%_test.rb"),
|
|
110
|
+
File.join(root_directory, FilePatterns::MODEL_TEST_DIR, "%MODEL_NAME%_test.rb"),
|
|
111
|
+
File.join(root_directory, FilePatterns::SPEC_MODEL_DIR, "%MODEL_NAME%_spec.rb")
|
|
112
|
+
]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def fixture_files(root_directory)
|
|
116
|
+
[
|
|
117
|
+
File.join(root_directory, FilePatterns::FIXTURE_TEST_DIR, "%TABLE_NAME%.yml"),
|
|
118
|
+
File.join(root_directory, FilePatterns::FIXTURE_SPEC_DIR, "%TABLE_NAME%.yml"),
|
|
119
|
+
File.join(root_directory, FilePatterns::FIXTURE_TEST_DIR, "%PLURALIZED_MODEL_NAME%.yml"),
|
|
120
|
+
File.join(root_directory, FilePatterns::FIXTURE_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.yml")
|
|
121
|
+
]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def scaffold_files(root_directory)
|
|
125
|
+
[
|
|
126
|
+
File.join(root_directory, FilePatterns::CONTROLLER_TEST_DIR, "%PLURALIZED_MODEL_NAME%_controller_test.rb"),
|
|
127
|
+
File.join(root_directory, FilePatterns::CONTROLLER_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_controller_spec.rb"),
|
|
128
|
+
File.join(root_directory, FilePatterns::REQUEST_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_spec.rb"),
|
|
129
|
+
File.join(root_directory, FilePatterns::ROUTING_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_routing_spec.rb")
|
|
130
|
+
]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def factory_files(root_directory)
|
|
134
|
+
[
|
|
135
|
+
File.join(root_directory, FilePatterns::EXEMPLARS_TEST_DIR, "%MODEL_NAME%_exemplar.rb"),
|
|
136
|
+
File.join(root_directory, FilePatterns::EXEMPLARS_SPEC_DIR, "%MODEL_NAME%_exemplar.rb"),
|
|
137
|
+
File.join(root_directory, FilePatterns::BLUEPRINTS_TEST_DIR, "%MODEL_NAME%_blueprint.rb"),
|
|
138
|
+
File.join(root_directory, FilePatterns::BLUEPRINTS_SPEC_DIR, "%MODEL_NAME%_blueprint.rb"),
|
|
139
|
+
File.join(root_directory, FilePatterns::FACTORY_BOT_TEST_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
|
|
140
|
+
File.join(root_directory, FilePatterns::FACTORY_BOT_SPEC_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
|
|
141
|
+
File.join(root_directory, FilePatterns::FACTORY_BOT_TEST_DIR, "%TABLE_NAME%.rb"), # (new style)
|
|
142
|
+
File.join(root_directory, FilePatterns::FACTORY_BOT_SPEC_DIR, "%TABLE_NAME%.rb"), # (new style)
|
|
143
|
+
File.join(root_directory, FilePatterns::FACTORY_BOT_TEST_DIR, "%PLURALIZED_MODEL_NAME%.rb"), # (new style)
|
|
144
|
+
File.join(root_directory, FilePatterns::FACTORY_BOT_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.rb"), # (new style)
|
|
145
|
+
File.join(root_directory, FilePatterns::FABRICATORS_TEST_DIR, "%MODEL_NAME%_fabricator.rb"),
|
|
146
|
+
File.join(root_directory, FilePatterns::FABRICATORS_SPEC_DIR, "%MODEL_NAME%_fabricator.rb")
|
|
147
|
+
]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def serialize_files(root_directory)
|
|
151
|
+
[
|
|
152
|
+
File.join(root_directory, FilePatterns::SERIALIZERS_DIR, "%MODEL_NAME%_serializer.rb"),
|
|
153
|
+
File.join(root_directory, FilePatterns::SERIALIZERS_TEST_DIR, "%MODEL_NAME%_serializer_test.rb"),
|
|
154
|
+
File.join(root_directory, FilePatterns::SERIALIZERS_SPEC_DIR, "%MODEL_NAME%_serializer_spec.rb")
|
|
155
|
+
]
|
|
156
|
+
end
|
|
25
157
|
end
|
|
26
158
|
end
|
|
27
159
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AnnotateRb
|
|
4
|
+
module ModelAnnotator
|
|
5
|
+
class ProjectAnnotationRemover
|
|
6
|
+
def initialize(options)
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def remove_annotations
|
|
11
|
+
project_model_files = model_files
|
|
12
|
+
|
|
13
|
+
removal_instructions = project_model_files.map do |path, filename|
|
|
14
|
+
file = File.join(path, filename)
|
|
15
|
+
|
|
16
|
+
if AnnotationDecider.new(file, @options).annotate?
|
|
17
|
+
_instructions = build_instructions_for_file(file)
|
|
18
|
+
end
|
|
19
|
+
end.flatten.compact
|
|
20
|
+
|
|
21
|
+
deannotated = removal_instructions.map do |instruction|
|
|
22
|
+
if SingleFileAnnotationRemover.call_with_instructions(instruction)
|
|
23
|
+
instruction.file
|
|
24
|
+
end
|
|
25
|
+
rescue => e
|
|
26
|
+
warn "Unable to process #{File.join(instruction.file)}: #{e.message}"
|
|
27
|
+
warn "\t" + e.backtrace.join("\n\t") if @options[:trace]
|
|
28
|
+
end.flatten.compact
|
|
29
|
+
|
|
30
|
+
if deannotated.empty?
|
|
31
|
+
puts "Model files unchanged."
|
|
32
|
+
else
|
|
33
|
+
puts "Removed annotations (#{deannotated.length}) from: #{deannotated.join(", ")}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def build_instructions_for_file(file)
|
|
40
|
+
klass = ModelClassGetter.call(file, @options)
|
|
41
|
+
|
|
42
|
+
instructions = []
|
|
43
|
+
|
|
44
|
+
klass.reset_column_information
|
|
45
|
+
model_name = klass.name.underscore
|
|
46
|
+
table_name = klass.table_name
|
|
47
|
+
|
|
48
|
+
model_instruction = SingleFileRemoveAnnotationInstruction.new(file, @options)
|
|
49
|
+
instructions << model_instruction
|
|
50
|
+
|
|
51
|
+
related_files = RelatedFilesListBuilder.new(file, model_name, table_name, @options).build
|
|
52
|
+
related_file_instructions = related_files.map do |f, _position_key|
|
|
53
|
+
_instruction = SingleFileRemoveAnnotationInstruction.new(f, @options)
|
|
54
|
+
end
|
|
55
|
+
instructions.concat(related_file_instructions)
|
|
56
|
+
|
|
57
|
+
instructions
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def model_files
|
|
61
|
+
@model_files ||= ModelFilesGetter.call(@options)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AnnotateRb
|
|
4
|
+
module ModelAnnotator
|
|
5
|
+
class ProjectAnnotator
|
|
6
|
+
def initialize(options)
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def annotate
|
|
11
|
+
project_model_files = model_files
|
|
12
|
+
|
|
13
|
+
annotation_instructions = project_model_files.map do |path, filename|
|
|
14
|
+
file = File.join(path, filename)
|
|
15
|
+
|
|
16
|
+
if AnnotationDecider.new(file, @options).annotate?
|
|
17
|
+
_instructions = build_instructions_for_file(file)
|
|
18
|
+
end
|
|
19
|
+
end.flatten.compact
|
|
20
|
+
|
|
21
|
+
annotated = annotation_instructions.map do |instruction|
|
|
22
|
+
if SingleFileAnnotator.call_with_instructions(instruction)
|
|
23
|
+
instruction.file
|
|
24
|
+
end
|
|
25
|
+
end.compact
|
|
26
|
+
|
|
27
|
+
if annotated.empty?
|
|
28
|
+
puts "Model files unchanged."
|
|
29
|
+
else
|
|
30
|
+
puts "Annotated (#{annotated.length}): #{annotated.join(", ")}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def build_instructions_for_file(file)
|
|
37
|
+
klass = ModelClassGetter.call(file, @options)
|
|
38
|
+
|
|
39
|
+
instructions = []
|
|
40
|
+
|
|
41
|
+
klass.reset_column_information
|
|
42
|
+
annotation = AnnotationBuilder.new(klass, @options).build
|
|
43
|
+
model_name = klass.name.underscore
|
|
44
|
+
table_name = klass.table_name
|
|
45
|
+
|
|
46
|
+
model_instruction = SingleFileAnnotatorInstruction.new(file, annotation, :position_in_class, @options)
|
|
47
|
+
instructions << model_instruction
|
|
48
|
+
|
|
49
|
+
related_files = RelatedFilesListBuilder.new(file, model_name, table_name, @options).build
|
|
50
|
+
related_file_instructions = related_files.map do |f, position_key|
|
|
51
|
+
_instruction = SingleFileAnnotatorInstruction.new(f, annotation, position_key, @options)
|
|
52
|
+
end
|
|
53
|
+
instructions.concat(related_file_instructions)
|
|
54
|
+
|
|
55
|
+
instructions
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def model_files
|
|
59
|
+
@model_files ||= ModelFilesGetter.call(@options)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|