annotaterb 4.16.0 → 4.18.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 +57 -0
- data/README.md +34 -5
- data/VERSION +1 -1
- data/lib/annotate_rb/model_annotator/annotated_file/generator.rb +14 -0
- data/lib/annotate_rb/model_annotator/annotation/annotation_builder.rb +25 -11
- data/lib/annotate_rb/model_annotator/annotation_decider.rb +4 -24
- data/lib/annotate_rb/model_annotator/annotation_diff_generator.rb +1 -2
- data/lib/annotate_rb/model_annotator/column_annotation/annotation_builder.rb +15 -34
- data/lib/annotate_rb/model_annotator/column_annotation/column_component.rb +34 -11
- data/lib/annotate_rb/model_annotator/index_annotation/annotation_builder.rb +1 -1
- data/lib/annotate_rb/model_annotator/index_annotation/index_component.rb +27 -4
- data/lib/annotate_rb/model_annotator/model_wrapper.rb +43 -4
- data/lib/annotate_rb/model_annotator/project_annotator.rb +5 -1
- data/lib/annotate_rb/options.rb +13 -2
- data/lib/annotate_rb/parser.rb +10 -0
- data/lib/annotate_rb/route_annotator/helper.rb +1 -1
- data/lib/annotate_rb/runner.rb +15 -1
- data/lib/annotate_rb/tasks/annotate_models_migrate.rake +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0276a5044aa1b444f73aac5612004ce9a8c05c6d47634fb2e8798c24a442fed5
|
4
|
+
data.tar.gz: 58cd27cb187989b06c459979ac0152db9dfdb54d671b9348980c244caa7981a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac0f491e7022299fc96029950c5edf2b0e05aa38e9f3198ec37acbd04275888be493f4202f31295e8db0ad7737cc03d2c6b52e5fbb51c24418ef92f40da77a13
|
7
|
+
data.tar.gz: 914216a38c137682acb6e2b58154c3af47dba3b0ebb424b69194a59c3939cfdd01c00d0f75a10bb51800f2ea6ce3f4dc97f3cdcd592ff7f77e7cafc906cd660f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,62 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v4.17.0](https://github.com/drwl/annotaterb/tree/v4.17.0) (2025-07-14)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/drwl/annotaterb/compare/v4.16.0...v4.17.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Place column comments at the end of the line \(feature suggestion\) [\#164](https://github.com/drwl/annotaterb/issues/164)
|
10
|
+
|
11
|
+
**Fixed bugs:**
|
12
|
+
|
13
|
+
- Model annotation chokes on an empty file [\#182](https://github.com/drwl/annotaterb/issues/182)
|
14
|
+
|
15
|
+
**Closed issues:**
|
16
|
+
|
17
|
+
- uninitialized constant Zeitwerk::VERSION [\#216](https://github.com/drwl/annotaterb/issues/216)
|
18
|
+
- Incorrect annotation of fixture files when different models share the same table name in different databases [\#206](https://github.com/drwl/annotaterb/issues/206)
|
19
|
+
- Should active record and active support be in the gemspec? [\#197](https://github.com/drwl/annotaterb/issues/197)
|
20
|
+
|
21
|
+
**Merged pull requests:**
|
22
|
+
|
23
|
+
- Bump version to v4.17.0 [\#234](https://github.com/drwl/annotaterb/pull/234) ([drwl](https://github.com/drwl))
|
24
|
+
- Fix NoMethodError when processing empty files [\#232](https://github.com/drwl/annotaterb/pull/232) ([tanukiti1987](https://github.com/tanukiti1987))
|
25
|
+
- Refactor column ignore to use `match?` [\#231](https://github.com/drwl/annotaterb/pull/231) ([tagliala](https://github.com/tagliala))
|
26
|
+
- Fix standard configuration [\#230](https://github.com/drwl/annotaterb/pull/230) ([tagliala](https://github.com/tagliala))
|
27
|
+
- Generate changelog for v4.16.0 [\#229](https://github.com/drwl/annotaterb/pull/229) ([drwl](https://github.com/drwl))
|
28
|
+
- show included columns in indexes [\#211](https://github.com/drwl/annotaterb/pull/211) ([pineman](https://github.com/pineman))
|
29
|
+
- fix: use model name for file retrieval when not connected to the primary DB. [\#207](https://github.com/drwl/annotaterb/pull/207) ([OdenTakashi](https://github.com/OdenTakashi))
|
30
|
+
- Place column comments at the end of the line [\#199](https://github.com/drwl/annotaterb/pull/199) ([Adeynack](https://github.com/Adeynack))
|
31
|
+
|
32
|
+
## [v4.16.0](https://github.com/drwl/annotaterb/tree/v4.16.0) (2025-06-18)
|
33
|
+
|
34
|
+
[Full Changelog](https://github.com/drwl/annotaterb/compare/v4.15.0...v4.16.0)
|
35
|
+
|
36
|
+
**Implemented enhancements:**
|
37
|
+
|
38
|
+
- Feature Request: Add Option to Place Annotations Above Nested Classes or Modules. [\#186](https://github.com/drwl/annotaterb/issues/186)
|
39
|
+
|
40
|
+
**Closed issues:**
|
41
|
+
|
42
|
+
- Misleading pattern examples in documentation for `additional_file_patterns` [\#221](https://github.com/drwl/annotaterb/issues/221)
|
43
|
+
- Permission denied for table pg\_index [\#209](https://github.com/drwl/annotaterb/issues/209)
|
44
|
+
- Performance regression relative to pre-fork? [\#205](https://github.com/drwl/annotaterb/issues/205)
|
45
|
+
- Add back ruby configuration option? [\#203](https://github.com/drwl/annotaterb/issues/203)
|
46
|
+
- Enable frozen mode when CI environment variable is set [\#171](https://github.com/drwl/annotaterb/issues/171)
|
47
|
+
|
48
|
+
**Merged pull requests:**
|
49
|
+
|
50
|
+
- Bump version to v4.16.0 [\#228](https://github.com/drwl/annotaterb/pull/228) ([drwl](https://github.com/drwl))
|
51
|
+
- chore: add --with-column-comments readme documentation [\#227](https://github.com/drwl/annotaterb/pull/227) ([jonmcelroy-appfolio](https://github.com/jonmcelroy-appfolio))
|
52
|
+
- Drop Ruby 2.7 support and improve CI [\#226](https://github.com/drwl/annotaterb/pull/226) ([drwl](https://github.com/drwl))
|
53
|
+
- Pass .annotaterb.yml through ERB [\#225](https://github.com/drwl/annotaterb/pull/225) ([fxn](https://github.com/fxn))
|
54
|
+
- feat: Add `--nested-position` option for placing annotations above nested classes. [\#223](https://github.com/drwl/annotaterb/pull/223) ([yamat47](https://github.com/yamat47))
|
55
|
+
- Fix for: Misleading pattern examples in documentation for additional\_file\_patterns [\#222](https://github.com/drwl/annotaterb/pull/222) ([skliarov](https://github.com/skliarov))
|
56
|
+
- Move activerecord dependency into gemspec [\#220](https://github.com/drwl/annotaterb/pull/220) ([drwl](https://github.com/drwl))
|
57
|
+
- Generate changelog for v4.15.0 [\#219](https://github.com/drwl/annotaterb/pull/219) ([drwl](https://github.com/drwl))
|
58
|
+
- chore: add rake task to automatically deploy to rubygems [\#183](https://github.com/drwl/annotaterb/pull/183) ([OdenTakashi](https://github.com/OdenTakashi))
|
59
|
+
|
3
60
|
## [v4.15.0](https://github.com/drwl/annotaterb/tree/v4.15.0) (2025-05-30)
|
4
61
|
|
5
62
|
[Full Changelog](https://github.com/drwl/annotaterb/compare/v4.14.1...v4.15.0)
|
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
## AnnotateRb
|
2
|
+
|
2
3
|
### forked from the [Annotate aka AnnotateModels gem](https://github.com/ctran/annotate_models)
|
3
4
|
|
4
5
|
A Ruby Gem that adds annotations to your Rails models and route files.
|
5
6
|
|
6
|
-
|
7
|
+
---
|
8
|
+
|
7
9
|
[](https://github.com/drwl/annotaterb/actions/workflows/ci.yml)
|
8
10
|
[](https://badge.fury.io/rb/annotaterb)
|
9
11
|
|
@@ -32,7 +34,9 @@ The schema comment looks like this:
|
|
32
34
|
class Task < ApplicationRecord
|
33
35
|
...
|
34
36
|
```
|
35
|
-
|
37
|
+
|
38
|
+
---
|
39
|
+
|
36
40
|
## Installation
|
37
41
|
|
38
42
|
```sh
|
@@ -46,11 +50,12 @@ group :development do
|
|
46
50
|
...
|
47
51
|
|
48
52
|
gem "annotaterb"
|
49
|
-
|
53
|
+
|
50
54
|
...
|
51
55
|
```
|
52
56
|
|
53
57
|
### Automatically annotate models
|
58
|
+
|
54
59
|
For Rails projects, model files can get automatically annotated after migration tasks. To do this, run the following command:
|
55
60
|
|
56
61
|
```sh
|
@@ -66,6 +71,7 @@ $ ANNOTATERB_SKIP_ON_DB_TASKS=1 bin/rails db:migrate
|
|
66
71
|
```
|
67
72
|
|
68
73
|
### Added Rails generators
|
74
|
+
|
69
75
|
The following Rails generator commands get added.
|
70
76
|
|
71
77
|
```sh
|
@@ -83,18 +89,23 @@ AnnotateRb:
|
|
83
89
|
```
|
84
90
|
|
85
91
|
`bin/rails g annotate_rb:config`
|
92
|
+
|
86
93
|
- Generates a new configuration file, `.annotaterb.yml`, using defaults from the gem.
|
87
94
|
|
88
95
|
`bin/rails g annotate_rb:hook`
|
96
|
+
|
89
97
|
- Installs the Rake file to automatically annotate Rails models on a database task (e.g. AnnotateRb will automatically run after running `bin/rails db:migrate`).
|
90
98
|
|
91
99
|
`bin/rails g annotate_rb:install`
|
100
|
+
|
92
101
|
- Runs the `config` and `hook` generator commands
|
93
102
|
|
94
103
|
`bin/rails g annotate_rb:update_config`
|
104
|
+
|
95
105
|
- Appends to `.annotaterb.yml` any configuration key-value pairs that are used by the Gem. This is useful when there's a drift between the config file values and the gem defaults (i.e. when new features get added).
|
96
106
|
|
97
107
|
## Migrating from the annotate gem
|
108
|
+
|
98
109
|
Refer to the [migration guide](MIGRATION_GUIDE.md).
|
99
110
|
|
100
111
|
## Usage
|
@@ -103,7 +114,7 @@ AnnotateRb has a CLI that you can use to add or remove annotations.
|
|
103
114
|
|
104
115
|
```sh
|
105
116
|
# To show the CLI options
|
106
|
-
$ bundle exec annotaterb
|
117
|
+
$ bundle exec annotaterb
|
107
118
|
|
108
119
|
Usage: annotaterb [command] [options]
|
109
120
|
|
@@ -127,6 +138,7 @@ Annotate model options:
|
|
127
138
|
Complete foreign key names in the annotation
|
128
139
|
-i, --show-indexes List the table's database indexes in the annotation
|
129
140
|
-s, --simple-indexes Concat the column's related indexes in the annotation
|
141
|
+
-c, --show-check-constraints List the table's check constraints in the annotation
|
130
142
|
--hide-limit-column-types VALUES
|
131
143
|
don't show limit for given column types, separated by commas (i.e., `integer,boolean,text`)
|
132
144
|
--hide-default-column-types VALUES
|
@@ -134,7 +146,15 @@ Annotate model options:
|
|
134
146
|
--ignore-unknown-models don't display warnings for bad model files
|
135
147
|
-I, --ignore-columns REGEX don't annotate columns that match a given REGEX (i.e., `annotate -I '^(id|updated_at|created_at)'`
|
136
148
|
--with-comment include database comments in model annotations
|
137
|
-
--
|
149
|
+
--without-comment include database comments in model annotations
|
150
|
+
--with-column-comments include column comments in model annotations
|
151
|
+
--without-column-comments exclude column comments in model annotations
|
152
|
+
--position-of-column-comments VALUE
|
153
|
+
set the position, in the annotation block, of the column comment
|
154
|
+
--with-table-comments include table comments in model annotations
|
155
|
+
--without-table-comments exclude table comments in model annotations
|
156
|
+
--classes-default-to-s class Custom classes to be represented with `to_s`, may be used multiple times
|
157
|
+
--nested-position Place annotations directly above nested classes or modules instead of at the top of the file.
|
138
158
|
|
139
159
|
Annotate routes options:
|
140
160
|
Usage: annotaterb routes [options]
|
@@ -157,6 +177,7 @@ Additional options that work for annotating models and routes
|
|
157
177
|
--ignore-model-subdirects Ignore subdirectories of the models directory
|
158
178
|
--sort Sort columns alphabetically, rather than in creation order
|
159
179
|
--classified-sort Sort columns alphabetically, but first goes id, then the rest columns, then the timestamp columns and then the association columns
|
180
|
+
--grouped-polymorphic Group polymorphic associations together in the annotation when using --classified-sort
|
160
181
|
-R, --require path Additional file to require before loading models, may be used multiple times
|
161
182
|
-e [tests,fixtures,factories,serializers],
|
162
183
|
--exclude Do not annotate fixtures, test files, factories, and/or serializers
|
@@ -176,6 +197,8 @@ Additional options that work for annotating models and routes
|
|
176
197
|
Place the annotations at the top (before) or the bottom (after) of the routes.rb file
|
177
198
|
--ps, --position-in-serializer [before|top|after|bottom]
|
178
199
|
Place the annotations at the top (before) or the bottom (after) of the serializer files
|
200
|
+
--pa, --position-in-additional-file-patterns [before|top|after|bottom]
|
201
|
+
Place the annotations at the top (before) or the bottom (after) of files captured in additional file patterns
|
179
202
|
--force Force new annotations even if there are no changes.
|
180
203
|
--debug Prints the options and outputs messages to make it easier to debug.
|
181
204
|
--frozen Do not allow to change annotations. Exits non-zero if there are going to be changes to files.
|
@@ -185,6 +208,7 @@ Additional options that work for annotating models and routes
|
|
185
208
|
## Configuration
|
186
209
|
|
187
210
|
### Storing default options
|
211
|
+
|
188
212
|
Previously in the [Annotate](https://github.com/ctran/annotate_models) you could pass options through the CLI or store them as environment variables. Annotaterb removes dependency on the environment variables and instead can read values from a `.annotaterb.yml` file stored in the Rails project root.
|
189
213
|
|
190
214
|
```yml
|
@@ -198,6 +222,7 @@ Annotaterb reads first the configuration file, if it exists, passes its content
|
|
198
222
|
For further details visit the [section in the migration guide](MIGRATION_GUIDE.md#automatic-annotations-after-running-database-migration-commands).
|
199
223
|
|
200
224
|
### How to skip annotating a particular model
|
225
|
+
|
201
226
|
If you want to always skip annotations on a particular model, add this string
|
202
227
|
anywhere in the file:
|
203
228
|
|
@@ -211,6 +236,10 @@ migrations were run).
|
|
211
236
|
If you prefer to sort alphabetically so that the results of annotation are
|
212
237
|
consistent regardless of what order migrations are executed in, use `--sort`.
|
213
238
|
|
239
|
+
You can also sort columns by type, then alphabetically using `--classified-sort`
|
240
|
+
and `--grouped-polymorphic`: first goes id, then the rest columns, then the
|
241
|
+
timestamp columns and then the association columns.
|
242
|
+
|
214
243
|
## License
|
215
244
|
|
216
245
|
Released under the same license as Ruby. No Support. No Warranty.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.
|
1
|
+
4.18.0
|
@@ -63,6 +63,9 @@ module AnnotateRb
|
|
63
63
|
# When nested_position is enabled, finds the most deeply nested class declaration
|
64
64
|
# to place annotations directly above nested classes instead of at the file top.
|
65
65
|
def determine_annotation_position(parsed)
|
66
|
+
# Handle empty files where no classes/modules are found
|
67
|
+
return [nil, 0] if parsed.starts.empty?
|
68
|
+
|
66
69
|
return parsed.starts.first unless @options[:nested_position]
|
67
70
|
|
68
71
|
class_entries = parsed.starts.select { |name, _line| parsed.type_map[name] == :class }
|
@@ -90,6 +93,17 @@ module AnnotateRb
|
|
90
93
|
def content_annotated_after(parsed, content_without_annotations)
|
91
94
|
_constant_name, line_number_after = parsed.ends.last
|
92
95
|
|
96
|
+
# Handle empty files where no classes/modules are found
|
97
|
+
if line_number_after.nil?
|
98
|
+
content_lines = content_without_annotations.lines
|
99
|
+
# For empty files, append annotations at the end
|
100
|
+
content_with_annotations_written_after = []
|
101
|
+
content_with_annotations_written_after << content_lines
|
102
|
+
content_with_annotations_written_after << $/ unless content_lines.empty?
|
103
|
+
content_with_annotations_written_after << @new_wrapped_annotations.lines
|
104
|
+
return content_with_annotations_written_after.join
|
105
|
+
end
|
106
|
+
|
93
107
|
content_with_annotations_written_after = []
|
94
108
|
content_with_annotations_written_after << content_without_annotations.lines[0..line_number_after]
|
95
109
|
content_with_annotations_written_after << $/
|
@@ -59,17 +59,7 @@ module AnnotateRb
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def build
|
62
|
-
|
63
|
-
migration_version = begin
|
64
|
-
ActiveRecord::Migrator.current_version
|
65
|
-
rescue
|
66
|
-
0
|
67
|
-
end
|
68
|
-
|
69
|
-
@options.set_state(:current_version, migration_version)
|
70
|
-
end
|
71
|
-
|
72
|
-
version = @options.get_state(:current_version)
|
62
|
+
version = migration_version_for_model(@model)
|
73
63
|
table_name = @model.table_name
|
74
64
|
table_comment = @model.connection.try(:table_comment, @model.table_name)
|
75
65
|
max_size = @model.max_schema_info_width
|
@@ -78,6 +68,30 @@ module AnnotateRb
|
|
78
68
|
version: version, table_name: table_name, table_comment: table_comment,
|
79
69
|
max_size: max_size, model: @model).build
|
80
70
|
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def migration_version_for_model(model)
|
75
|
+
return 0 unless @options[:include_version]
|
76
|
+
|
77
|
+
# Multi-database support: Cache migration versions per database connection to handle
|
78
|
+
# different schema versions across primary/secondary databases correctly.
|
79
|
+
# Example: primary → "current_version_primary", secondary → "current_version_secondary"
|
80
|
+
connection_pool_name = model.connection.pool.db_config.name
|
81
|
+
cache_key = "current_version_#{connection_pool_name}".to_sym
|
82
|
+
|
83
|
+
if @options.get_state(cache_key).nil?
|
84
|
+
migration_version = begin
|
85
|
+
model.connection.migration_context.current_version
|
86
|
+
rescue
|
87
|
+
0
|
88
|
+
end
|
89
|
+
|
90
|
+
@options.set_state(cache_key, migration_version)
|
91
|
+
end
|
92
|
+
|
93
|
+
@options.get_state(cache_key)
|
94
|
+
end
|
81
95
|
end
|
82
96
|
end
|
83
97
|
end
|
@@ -16,29 +16,9 @@ module AnnotateRb
|
|
16
16
|
|
17
17
|
begin
|
18
18
|
klass = ModelClassGetter.call(@file, @options)
|
19
|
+
return false unless klass.respond_to?(:descends_from_active_record?) && klass.descends_from_active_record? && klass.table_exists?
|
19
20
|
|
20
|
-
|
21
|
-
# Methods such as #superclass only exist on a class. Because of how the code is structured, `klass` could be a
|
22
|
-
# module that does not support the #superclass method, so we want to return early.
|
23
|
-
return false if !klass_is_a_class
|
24
|
-
|
25
|
-
klass_inherits_active_record_base = klass < ActiveRecord::Base
|
26
|
-
klass_is_not_abstract = klass.respond_to?(:abstract_class?) && !klass.abstract_class?
|
27
|
-
klass_table_exists = klass.respond_to?(:table_exists?) && klass.table_exists?
|
28
|
-
|
29
|
-
not_sure_this_conditional = (!@options[:exclude_sti_subclasses] || !(klass.superclass < ActiveRecord::Base && klass.table_name == klass.superclass.table_name))
|
30
|
-
|
31
|
-
annotate_conditions = [
|
32
|
-
klass_is_a_class,
|
33
|
-
klass_inherits_active_record_base,
|
34
|
-
not_sure_this_conditional,
|
35
|
-
klass_is_not_abstract,
|
36
|
-
klass_table_exists
|
37
|
-
]
|
38
|
-
|
39
|
-
to_annotate = annotate_conditions.all?
|
40
|
-
|
41
|
-
return to_annotate
|
21
|
+
return @options[:exclude_sti_subclasses] ? klass.base_class? : true
|
42
22
|
rescue BadModelFileError => e
|
43
23
|
unless @options[:ignore_unknown_models]
|
44
24
|
warn "Unable to process #{@file}: #{e.message}"
|
@@ -55,9 +35,9 @@ module AnnotateRb
|
|
55
35
|
private
|
56
36
|
|
57
37
|
def file_contains_skip_annotation
|
58
|
-
|
38
|
+
return false unless File.exist?(@file)
|
59
39
|
|
60
|
-
/#{SKIP_ANNOTATION_PREFIX}.*/o.match?(
|
40
|
+
/#{SKIP_ANNOTATION_PREFIX}.*/o.match?(File.read(@file))
|
61
41
|
end
|
62
42
|
end
|
63
43
|
end
|
@@ -11,8 +11,7 @@ module AnnotateRb
|
|
11
11
|
# - "# status(a/b/c) :string not null"
|
12
12
|
# - "# created_at :datetime not null"
|
13
13
|
# - "# updated_at :datetime not null"
|
14
|
-
COLUMN_PATTERN = /^#[\t ]+[\
|
15
|
-
|
14
|
+
COLUMN_PATTERN = /^#[\t ]+[[\p{L}\p{N}_]*.`\[\]():]+(?:\(.*?\))?[\t ]+.+$/
|
16
15
|
class << self
|
17
16
|
def call(file_content, annotation_block)
|
18
17
|
new(file_content, annotation_block).generate
|
@@ -12,42 +12,23 @@ module AnnotateRb
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def build
|
15
|
-
|
16
|
-
|
17
|
-
table_indices = @model.retrieve_indexes_from_table
|
18
|
-
column_indices = table_indices.select { |ind| ind.columns.include?(@column.name) }
|
19
|
-
column_defaults = @model.column_defaults
|
20
|
-
|
21
|
-
column_attributes = AttributesBuilder.new(@column, @options, is_primary_key, column_indices, column_defaults).build
|
22
|
-
formatted_column_type = TypeBuilder.new(@column, @options, column_defaults).build
|
15
|
+
column_attributes = @model.built_attributes[@column.name]
|
16
|
+
formatted_column_type = TypeBuilder.new(@column, @options, @model.column_defaults).build
|
23
17
|
|
24
18
|
display_column_comments = @options[:with_comment] && @options[:with_column_comments]
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
if model.primary_key
|
39
|
-
if model.primary_key.is_a?(Array)
|
40
|
-
# If the model has multiple primary keys, check if this column is one of them
|
41
|
-
if model.primary_key.collect(&:to_sym).include?(column_name.to_sym)
|
42
|
-
return true
|
43
|
-
end
|
44
|
-
elsif column_name.to_sym == model.primary_key.to_sym
|
45
|
-
# If model has 1 primary key, check if this column is it
|
46
|
-
return true
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
false
|
19
|
+
display_column_comments &&= @model.with_comments? && @column.comment
|
20
|
+
position_of_column_comment = @options[:position_of_column_comment] || Options::FLAG_OPTIONS[:position_of_column_comment] if display_column_comments
|
21
|
+
|
22
|
+
max_attributes_size = @model.built_attributes.values.map { |v| v.join(", ").length }.max
|
23
|
+
|
24
|
+
_component = ColumnComponent.new(
|
25
|
+
column: @column,
|
26
|
+
max_name_size: @max_size,
|
27
|
+
type: formatted_column_type,
|
28
|
+
attributes: column_attributes,
|
29
|
+
position_of_column_comment: position_of_column_comment,
|
30
|
+
max_attributes_size: max_attributes_size
|
31
|
+
)
|
51
32
|
end
|
52
33
|
end
|
53
34
|
end
|
@@ -6,19 +6,31 @@ module AnnotateRb
|
|
6
6
|
class ColumnComponent < Components::Base
|
7
7
|
MD_TYPE_ALLOWANCE = 18
|
8
8
|
BARE_TYPE_ALLOWANCE = 16
|
9
|
+
MIN_SPACES_BEFORE_COMMENT = 4
|
9
10
|
|
10
|
-
attr_reader :
|
11
|
+
attr_reader :column, :max_name_size, :type, :attributes, :position_of_column_comment, :max_attributes_size
|
11
12
|
|
12
|
-
def initialize(
|
13
|
-
@
|
14
|
-
@
|
13
|
+
def initialize(column:, max_name_size:, type:, attributes:, position_of_column_comment:, max_attributes_size:)
|
14
|
+
@column = column
|
15
|
+
@max_name_size = max_name_size
|
15
16
|
@type = type
|
16
17
|
@attributes = attributes
|
18
|
+
@position_of_column_comment = position_of_column_comment
|
19
|
+
@max_attributes_size = max_attributes_size
|
20
|
+
end
|
21
|
+
|
22
|
+
def name
|
23
|
+
case position_of_column_comment
|
24
|
+
when :with_name
|
25
|
+
"#{column.name}(#{column.comment.gsub(/\n/, '\\n')})"
|
26
|
+
else
|
27
|
+
column.name
|
28
|
+
end
|
17
29
|
end
|
18
30
|
|
19
31
|
def to_rdoc
|
20
32
|
# standard:disable Lint/FormatParameterMismatch
|
21
|
-
format("# %-#{
|
33
|
+
format("# %-#{max_name_size}.#{max_name_size}s<tt>%s</tt>",
|
22
34
|
"*#{name}*::",
|
23
35
|
attributes.unshift(type).join(", ")).rstrip
|
24
36
|
# standard:enable Lint/FormatParameterMismatch
|
@@ -40,24 +52,35 @@ module AnnotateRb
|
|
40
52
|
end
|
41
53
|
|
42
54
|
def to_markdown
|
43
|
-
|
55
|
+
joined_attributes = attributes.join(", ").rstrip
|
56
|
+
name_remainder = max_name_size - name.length - non_ascii_length(name)
|
44
57
|
type_remainder = (MD_TYPE_ALLOWANCE - 2) - type.length
|
58
|
+
attributes_remainder = max_attributes_size + 1 - joined_attributes.length
|
59
|
+
comment_rightmost = (position_of_column_comment != :rightmost_column) ? "" : " | `#{@column.comment}`"
|
45
60
|
|
46
61
|
# standard:disable Lint/FormatParameterMismatch
|
47
|
-
format(
|
62
|
+
format(
|
63
|
+
"# **`%s`**%#{name_remainder}s | `%s`%#{type_remainder}s | `%s`%#{attributes_remainder}s", # %s",
|
48
64
|
name,
|
49
65
|
" ",
|
50
66
|
type,
|
51
67
|
" ",
|
52
|
-
|
68
|
+
joined_attributes,
|
69
|
+
comment_rightmost.to_s
|
70
|
+
).gsub("``", " ").rstrip
|
53
71
|
# standard:enable Lint/FormatParameterMismatch
|
54
72
|
end
|
55
73
|
|
56
74
|
def to_default
|
57
|
-
|
58
|
-
|
75
|
+
comment_rightmost = (position_of_column_comment == :rightmost_column) ? @column.comment : ""
|
76
|
+
joined_attributes = attributes.join(", ")
|
77
|
+
format(
|
78
|
+
"# %s:%s %s %s",
|
79
|
+
mb_chars_ljust(name, max_name_size),
|
59
80
|
mb_chars_ljust(type, BARE_TYPE_ALLOWANCE),
|
60
|
-
|
81
|
+
mb_chars_ljust(joined_attributes, max_attributes_size.to_i + MIN_SPACES_BEFORE_COMMENT),
|
82
|
+
comment_rightmost
|
83
|
+
).rstrip
|
61
84
|
end
|
62
85
|
|
63
86
|
private
|
@@ -18,7 +18,7 @@ module AnnotateRb
|
|
18
18
|
max_size = indexes.map { |index| index.name.size }.max + 1
|
19
19
|
|
20
20
|
indexes = indexes.sort_by(&:name).map do |index|
|
21
|
-
IndexComponent.new(index, max_size)
|
21
|
+
IndexComponent.new(index, max_size, @options)
|
22
22
|
end
|
23
23
|
|
24
24
|
_annotation = Annotation.new(indexes)
|
@@ -4,11 +4,12 @@ module AnnotateRb
|
|
4
4
|
module ModelAnnotator
|
5
5
|
module IndexAnnotation
|
6
6
|
class IndexComponent < Components::Base
|
7
|
-
attr_reader :index, :max_size
|
7
|
+
attr_reader :index, :max_size, :options
|
8
8
|
|
9
|
-
def initialize(index, max_size)
|
9
|
+
def initialize(index, max_size, options)
|
10
10
|
@index = index
|
11
11
|
@max_size = max_size
|
12
|
+
@options = options
|
12
13
|
end
|
13
14
|
|
14
15
|
def to_default
|
@@ -34,11 +35,22 @@ module AnnotateRb
|
|
34
35
|
""
|
35
36
|
end
|
36
37
|
|
38
|
+
include_info = ""
|
39
|
+
if options[:show_indexes_include]
|
40
|
+
value = index.try(:include)
|
41
|
+
include_info = if value.present? && value.any?
|
42
|
+
" INCLUDE (#{value.join(",")})"
|
43
|
+
else
|
44
|
+
""
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
37
48
|
# standard:disable Lint/FormatParameterMismatch
|
38
49
|
sprintf(
|
39
|
-
"# %-#{max_size}.#{max_size}s %s%s%s%s%s",
|
50
|
+
"# %-#{max_size}.#{max_size}s %s%s%s%s%s%s",
|
40
51
|
index.name,
|
41
52
|
"(#{columns_info.join(",")})",
|
53
|
+
include_info,
|
42
54
|
unique_info,
|
43
55
|
nulls_not_distinct_info,
|
44
56
|
where_info,
|
@@ -70,8 +82,19 @@ module AnnotateRb
|
|
70
82
|
""
|
71
83
|
end
|
72
84
|
|
85
|
+
include_info = ""
|
86
|
+
if options[:show_indexes_include]
|
87
|
+
value = index.try(:include)
|
88
|
+
include_info = if value.present? && value.any?
|
89
|
+
" _include_ (#{value.join(",")})"
|
90
|
+
else
|
91
|
+
""
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
73
95
|
details = sprintf(
|
74
|
-
"%s%s%s%s",
|
96
|
+
"%s%s%s%s%s",
|
97
|
+
include_info,
|
75
98
|
unique_info,
|
76
99
|
nulls_not_distinct_info,
|
77
100
|
where_info,
|
@@ -23,12 +23,12 @@ module AnnotateRb
|
|
23
23
|
ignore_columns = @options[:ignore_columns]
|
24
24
|
if ignore_columns
|
25
25
|
cols = cols.reject do |col|
|
26
|
-
col.name.match(/#{ignore_columns}/)
|
26
|
+
col.name.match?(/#{ignore_columns}/)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
cols = cols.sort_by(&:name) if @options[:sort]
|
31
|
-
cols = classified_sort(cols) if @options[:classified_sort]
|
31
|
+
cols = classified_sort(cols, @options[:grouped_polymorphic]) if @options[:classified_sort]
|
32
32
|
|
33
33
|
cols
|
34
34
|
end
|
@@ -91,7 +91,8 @@ module AnnotateRb
|
|
91
91
|
begin
|
92
92
|
cols = columns
|
93
93
|
|
94
|
-
|
94
|
+
position_of_column_comment = @options.with_default_fallback(:position_of_column_comment)
|
95
|
+
if with_comments? && position_of_column_comment == :with_name
|
95
96
|
column_widths = cols.map do |column|
|
96
97
|
column.name.size + (column.comment ? Helper.width(column.comment) : 0)
|
97
98
|
end
|
@@ -108,6 +109,35 @@ module AnnotateRb
|
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
112
|
+
# TODO: Simplify this conditional
|
113
|
+
def is_column_primary_key?(column_name)
|
114
|
+
if primary_key
|
115
|
+
if primary_key.is_a?(Array)
|
116
|
+
# If the model has multiple primary keys, check if this column is one of them
|
117
|
+
if primary_key.collect(&:to_sym).include?(column_name.to_sym)
|
118
|
+
return true
|
119
|
+
end
|
120
|
+
elsif column_name.to_sym == primary_key.to_sym
|
121
|
+
# If model has 1 primary key, check if this column is it
|
122
|
+
return true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def built_attributes
|
130
|
+
@built_attributes ||= begin
|
131
|
+
table_indices = retrieve_indexes_from_table
|
132
|
+
columns.map do |column|
|
133
|
+
is_primary_key = is_column_primary_key?(column.name)
|
134
|
+
column_indices = table_indices.select { |ind| ind.columns.include?(column.name) }
|
135
|
+
built = ColumnAnnotation::AttributesBuilder.new(column, @options, is_primary_key, column_indices, column_defaults).build
|
136
|
+
[column.name, built]
|
137
|
+
end.to_h
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
111
141
|
def retrieve_indexes_from_table
|
112
142
|
@indexes_from_table ||= _retrieve_indexes_from_table
|
113
143
|
end
|
@@ -143,7 +173,11 @@ module AnnotateRb
|
|
143
173
|
raw_columns.map(&:comment).any? { |comment| !comment.nil? }
|
144
174
|
end
|
145
175
|
|
146
|
-
def
|
176
|
+
def position_of_column_comment
|
177
|
+
@position_of_column_comment ||= @options[:position_of_column_comment]
|
178
|
+
end
|
179
|
+
|
180
|
+
def classified_sort(cols, grouped_polymorphic)
|
147
181
|
rest_cols = []
|
148
182
|
timestamps = []
|
149
183
|
associations = []
|
@@ -152,6 +186,8 @@ module AnnotateRb
|
|
152
186
|
# specs don't load defaults, so ensure we have defaults here
|
153
187
|
timestamp_columns = @options[:timestamp_columns] || DEFAULT_TIMESTAMP_COLUMNS
|
154
188
|
|
189
|
+
col_names = cols.map(&:name)
|
190
|
+
|
155
191
|
cols.each do |c|
|
156
192
|
if c.name.eql?("id")
|
157
193
|
id = c
|
@@ -159,6 +195,9 @@ module AnnotateRb
|
|
159
195
|
timestamps << c
|
160
196
|
elsif c.name[-3, 3].eql?("_id")
|
161
197
|
associations << c
|
198
|
+
elsif c.name[-5, 5].eql?("_type") && col_names.include?(c.name.sub(/_type$/, "_id")) && grouped_polymorphic
|
199
|
+
# This is a polymorphic association's type column
|
200
|
+
associations << c
|
162
201
|
else
|
163
202
|
rest_cols << c
|
164
203
|
end
|
@@ -42,7 +42,11 @@ module AnnotateRb
|
|
42
42
|
klass.reset_column_information
|
43
43
|
annotation = Annotation::AnnotationBuilder.new(klass, @options).build
|
44
44
|
model_name = klass.name.underscore
|
45
|
-
|
45
|
+
|
46
|
+
# In multi-database configurations, it is possible for different models to have the same table name but live
|
47
|
+
# in different databases. Here we are opting to use the table name to find related files only when the model
|
48
|
+
# is using the primary connection.
|
49
|
+
table_name = klass.table_name if klass.connection_specification_name == ActiveRecord::Base.name
|
46
50
|
|
47
51
|
model_instruction = SingleFileAnnotatorInstruction.new(file, annotation, :position_in_class, @options)
|
48
52
|
instructions << model_instruction
|
data/lib/annotate_rb/options.rb
CHANGED
@@ -41,6 +41,7 @@ module AnnotateRb
|
|
41
41
|
format_rdoc: false, # ModelAnnotator
|
42
42
|
format_yard: false, # ModelAnnotator
|
43
43
|
frozen: false, # ModelAnnotator, but should be used by both
|
44
|
+
grouped_polymorphic: false, # ModelAnnotator
|
44
45
|
ignore_model_sub_dir: false, # ModelAnnotator
|
45
46
|
ignore_unknown_models: false, # ModelAnnotator
|
46
47
|
include_version: false, # ModelAnnotator
|
@@ -48,6 +49,7 @@ module AnnotateRb
|
|
48
49
|
show_check_constraints: false, # ModelAnnotator
|
49
50
|
show_foreign_keys: true, # ModelAnnotator
|
50
51
|
show_indexes: true, # ModelAnnotator
|
52
|
+
show_indexes_include: false, # ModelAnnotator
|
51
53
|
show_virtual_columns: false, # ModelAnnotator
|
52
54
|
simple_indexes: false, # ModelAnnotator
|
53
55
|
sort: false, # ModelAnnotator
|
@@ -55,7 +57,8 @@ module AnnotateRb
|
|
55
57
|
trace: false, # ModelAnnotator, but is part of Core
|
56
58
|
with_comment: true, # ModelAnnotator
|
57
59
|
with_column_comments: nil, # ModelAnnotator
|
58
|
-
with_table_comments: nil # ModelAnnotator
|
60
|
+
with_table_comments: nil, # ModelAnnotator
|
61
|
+
position_of_column_comment: :with_name # ModelAnnotator
|
59
62
|
}.freeze
|
60
63
|
|
61
64
|
OTHER_OPTIONS = {
|
@@ -111,6 +114,7 @@ module AnnotateRb
|
|
111
114
|
:format_rdoc,
|
112
115
|
:format_yard,
|
113
116
|
:frozen,
|
117
|
+
:grouped_polymorphic,
|
114
118
|
:ignore_model_sub_dir,
|
115
119
|
:ignore_unknown_models,
|
116
120
|
:include_version,
|
@@ -118,13 +122,15 @@ module AnnotateRb
|
|
118
122
|
:show_complete_foreign_keys,
|
119
123
|
:show_foreign_keys,
|
120
124
|
:show_indexes,
|
125
|
+
:show_indexes_include,
|
121
126
|
:simple_indexes,
|
122
127
|
:sort,
|
123
128
|
:timestamp,
|
124
129
|
:trace,
|
125
130
|
:with_comment,
|
126
131
|
:with_column_comments,
|
127
|
-
:with_table_comments
|
132
|
+
:with_table_comments,
|
133
|
+
:position_of_column_comment
|
128
134
|
].freeze
|
129
135
|
|
130
136
|
OTHER_OPTION_KEYS = [
|
@@ -205,10 +211,15 @@ module AnnotateRb
|
|
205
211
|
# Set column and table comments to default to :with_comment, if not set
|
206
212
|
@options[:with_column_comments] = @options[:with_comment] if @options[:with_column_comments].nil?
|
207
213
|
@options[:with_table_comments] = @options[:with_comment] if @options[:with_table_comments].nil?
|
214
|
+
@options[:position_of_column_comment] = @options[:position_of_column_comment].to_sym
|
208
215
|
|
209
216
|
self
|
210
217
|
end
|
211
218
|
|
219
|
+
def with_default_fallback(key)
|
220
|
+
@options[key] || DEFAULT_OPTIONS[key]
|
221
|
+
end
|
222
|
+
|
212
223
|
def set_state(key, value, overwrite = false)
|
213
224
|
if @state.key?(key) && !overwrite
|
214
225
|
val = @state[key]
|
data/lib/annotate_rb/parser.rb
CHANGED
@@ -246,6 +246,11 @@ module AnnotateRb
|
|
246
246
|
@options[:with_column_comments] = false
|
247
247
|
end
|
248
248
|
|
249
|
+
option_parser.on("--position-of-column-comments VALUE",
|
250
|
+
"set the position, in the annotation block, of the column comment") do |value|
|
251
|
+
@options[:position_of_column_comments] = value.to_sym
|
252
|
+
end
|
253
|
+
|
249
254
|
option_parser.on("--with-table-comments",
|
250
255
|
"include table comments in model annotations") do
|
251
256
|
@options[:with_table_comments] = true
|
@@ -402,6 +407,11 @@ module AnnotateRb
|
|
402
407
|
@options[:classified_sort] = true
|
403
408
|
end
|
404
409
|
|
410
|
+
option_parser.on("--grouped-polymorphic",
|
411
|
+
"Group polymorphic associations together in the annotation when using --classified-sort") do
|
412
|
+
@options[:grouped_polymorphic] = true
|
413
|
+
end
|
414
|
+
|
405
415
|
option_parser.on("-R",
|
406
416
|
"--require path",
|
407
417
|
"Additional file to require before loading models, may be used multiple times") do |path|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module AnnotateRb
|
4
4
|
module RouteAnnotator
|
5
5
|
module Helper
|
6
|
-
MAGIC_COMMENT_MATCHER = /(^#\s*encoding:.*)|(^# coding:.*)|(^# -\*- coding:.*)|(^# -\*- encoding\s?:.*)|(^#\s*frozen_string_literal:.+)|(^# -\*- frozen_string_literal\s*:.+-\*-)
|
6
|
+
MAGIC_COMMENT_MATCHER = /(^#\s*encoding:.*)|(^# coding:.*)|(^# -\*- coding:.*)|(^# -\*- encoding\s?:.*)|(^#\s*frozen_string_literal:.+)|(^# -\*- frozen_string_literal\s*:.+-\*-)/
|
7
7
|
|
8
8
|
class << self
|
9
9
|
def routes_file_exist?
|
data/lib/annotate_rb/runner.rb
CHANGED
@@ -3,9 +3,23 @@
|
|
3
3
|
module AnnotateRb
|
4
4
|
class Runner
|
5
5
|
class << self
|
6
|
+
attr_reader :runner
|
7
|
+
|
6
8
|
def run(args)
|
7
|
-
new
|
9
|
+
self.runner = new
|
10
|
+
|
11
|
+
runner.run(args)
|
12
|
+
|
13
|
+
self.runner = nil
|
8
14
|
end
|
15
|
+
|
16
|
+
def running?
|
17
|
+
!!runner
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_writer :runner
|
9
23
|
end
|
10
24
|
|
11
25
|
def run(args)
|
@@ -18,7 +18,7 @@ if defined?(Rails::Application) && Rails.version.split(".").first.to_i >= 6
|
|
18
18
|
|
19
19
|
# If there's multiple databases, this appends database specific rake tasks to `migration_tasks`
|
20
20
|
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |database_name|
|
21
|
-
migration_tasks.concat(%w[db:migrate db:migrate:up db:migrate:down].map { |task| "#{task}:#{database_name}" })
|
21
|
+
migration_tasks.concat(%w[db:migrate db:migrate:up db:migrate:down db:rollback].map { |task| "#{task}:#{database_name}" })
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: annotaterb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew W. Lee
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|