annotaterb 4.22.0 → 4.24.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 +83 -0
- data/README.md +156 -4
- data/VERSION +1 -1
- data/lib/annotate_rb/helper.rb +24 -1
- data/lib/annotate_rb/model_annotator/annotated_file/generator.rb +34 -12
- data/lib/annotate_rb/model_annotator/annotated_file/updater.rb +14 -1
- data/lib/annotate_rb/model_annotator/annotation/annotation_builder.rb +9 -1
- data/lib/annotate_rb/model_annotator/annotation_decider.rb +3 -0
- data/lib/annotate_rb/model_annotator/annotation_diff_generator.rb +9 -4
- data/lib/annotate_rb/model_annotator/column_annotation/attributes_builder.rb +7 -0
- data/lib/annotate_rb/model_annotator/column_annotation/column_component.rb +2 -2
- data/lib/annotate_rb/model_annotator/column_annotation/column_wrapper.rb +4 -0
- data/lib/annotate_rb/model_annotator/enum_annotation/annotation.rb +40 -0
- data/lib/annotate_rb/model_annotator/enum_annotation/annotation_builder.rb +38 -0
- data/lib/annotate_rb/model_annotator/enum_annotation/enum_component.rb +27 -0
- data/lib/annotate_rb/model_annotator/enum_annotation.rb +11 -0
- data/lib/annotate_rb/model_annotator/exclusion_constraint_annotation/annotation.rb +40 -0
- data/lib/annotate_rb/model_annotator/exclusion_constraint_annotation/annotation_builder.rb +38 -0
- data/lib/annotate_rb/model_annotator/exclusion_constraint_annotation/exclusion_constraint_component.rb +27 -0
- data/lib/annotate_rb/model_annotator/exclusion_constraint_annotation.rb +11 -0
- data/lib/annotate_rb/model_annotator/file_parser/annotation_finder.rb +75 -4
- data/lib/annotate_rb/model_annotator/file_parser/annotation_target.rb +31 -0
- data/lib/annotate_rb/model_annotator/file_parser/custom_parser.rb +11 -4
- data/lib/annotate_rb/model_annotator/file_parser/magic_comment.rb +32 -0
- data/lib/annotate_rb/model_annotator/file_parser/parsed_file.rb +5 -6
- data/lib/annotate_rb/model_annotator/file_parser/yml_parser.rb +43 -4
- data/lib/annotate_rb/model_annotator/file_parser.rb +2 -0
- data/lib/annotate_rb/model_annotator/foreign_key_annotation/foreign_key_component_builder.rb +6 -0
- data/lib/annotate_rb/model_annotator/index_annotation/annotation_builder.rb +29 -0
- data/lib/annotate_rb/model_annotator/index_annotation/index_component.rb +26 -4
- data/lib/annotate_rb/model_annotator/model_wrapper.rb +58 -2
- data/lib/annotate_rb/model_annotator/project_annotator.rb +2 -1
- data/lib/annotate_rb/model_annotator/single_file_annotation_remover.rb +1 -1
- data/lib/annotate_rb/model_annotator/single_file_annotator.rb +6 -6
- data/lib/annotate_rb/model_annotator/single_file_annotator_instruction.rb +3 -2
- data/lib/annotate_rb/model_annotator/unique_constraint_annotation/annotation.rb +40 -0
- data/lib/annotate_rb/model_annotator/unique_constraint_annotation/annotation_builder.rb +37 -0
- data/lib/annotate_rb/model_annotator/unique_constraint_annotation/unique_constraint_component.rb +27 -0
- data/lib/annotate_rb/model_annotator/unique_constraint_annotation.rb +11 -0
- data/lib/annotate_rb/model_annotator/zeitwerk_class_getter.rb +1 -1
- data/lib/annotate_rb/model_annotator.rb +3 -0
- data/lib/annotate_rb/options.rb +12 -2
- data/lib/annotate_rb/parser.rb +23 -6
- data/lib/annotate_rb/route_annotator/base_processor.rb +1 -1
- data/lib/annotate_rb/route_annotator/helper.rb +1 -1
- data/lib/annotate_rb/runner.rb +12 -6
- data/lib/annotate_rb/tasks/annotate_models_migrate.rake +8 -3
- data/lib/generators/annotate_rb/update_config/update_config_generator.rb +4 -1
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff11e0177eddc61744806ec5749c7a5d234023e0e7d75d46bd5fc21e4d0998fc
|
|
4
|
+
data.tar.gz: f8c87d260bf6cafbeb120568ecda6131f6bd335ef2c3882630a45cc2e6631a05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6e2edbfc22db4f8b1bd150313e795993e3f5366ac4594084fec2f7ee9f1d2650d98cef0933bdc92a0ff0a4e52af4bb77b35672cbd0049fed9c8e6c256743e00e
|
|
7
|
+
data.tar.gz: ddf3161a49b9c041083ce42c9264823da16e23703a8f013b98098f24dc4f7d46642a659621f0d0612572865fec1099a91e7622139acc3a1c689e7df1d96e81bf
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,88 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v4.23.0](https://github.com/drwl/annotaterb/tree/v4.23.0) (2026-06-25)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/drwl/annotaterb/compare/v4.22.0...v4.23.0)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- \[Request\] Support for multiple serializers for one model [\#329](https://github.com/drwl/annotaterb/issues/329)
|
|
10
|
+
- Support COMMENT on an INDEX [\#258](https://github.com/drwl/annotaterb/issues/258)
|
|
11
|
+
|
|
12
|
+
**Fixed bugs:**
|
|
13
|
+
|
|
14
|
+
- Re-running annotaterb strips indentation from nested \(indented\) schema annotations [\#332](https://github.com/drwl/annotaterb/issues/332)
|
|
15
|
+
- Setting all sort options to `false` sometimes doesn't respect migration/DB order [\#237](https://github.com/drwl/annotaterb/issues/237)
|
|
16
|
+
|
|
17
|
+
**Closed issues:**
|
|
18
|
+
|
|
19
|
+
- mistake post please delete this [\#314](https://github.com/drwl/annotaterb/issues/314)
|
|
20
|
+
- Removal of final empty comment line after foreign keys results in duplicate `fk_rails_...` line [\#297](https://github.com/drwl/annotaterb/issues/297)
|
|
21
|
+
- Default settings does not indent correctly inside a module [\#271](https://github.com/drwl/annotaterb/issues/271)
|
|
22
|
+
- Top does not do what I think it should? And annotator fails to indent. [\#202](https://github.com/drwl/annotaterb/issues/202)
|
|
23
|
+
- annotaterb is moving my class doc [\#201](https://github.com/drwl/annotaterb/issues/201)
|
|
24
|
+
|
|
25
|
+
**Merged pull requests:**
|
|
26
|
+
|
|
27
|
+
- Bump version to v4.23.0 [\#347](https://github.com/drwl/annotaterb/pull/347) ([drwl](https://github.com/drwl))
|
|
28
|
+
- Bump actions/checkout from 6 to 7 [\#346](https://github.com/drwl/annotaterb/pull/346) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
29
|
+
- Fix duplicate annotations when a file ends with a trailing blank line [\#342](https://github.com/drwl/annotaterb/pull/342) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
30
|
+
- Fix update\_config for non-default config locations [\#341](https://github.com/drwl/annotaterb/pull/341) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
31
|
+
- Fix NameError in ZeitwerkClassGetter for collapsed Zeitwerk paths [\#339](https://github.com/drwl/annotaterb/pull/339) ([KaiyuanMa](https://github.com/KaiyuanMa))
|
|
32
|
+
- Fix annotation inserted inside module when doc comment is present [\#338](https://github.com/drwl/annotaterb/pull/338) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
33
|
+
- Add `position_in_class: after_doc` to keep class doc adjacent to class [\#337](https://github.com/drwl/annotaterb/pull/337) ([yamat47](https://github.com/yamat47))
|
|
34
|
+
- Fix annotation placement above inner class declarations under `nested_position` [\#336](https://github.com/drwl/annotaterb/pull/336) ([yamat47](https://github.com/yamat47))
|
|
35
|
+
- Drop Ruby \< 3.3 from CI matrix [\#335](https://github.com/drwl/annotaterb/pull/335) ([drwl](https://github.com/drwl))
|
|
36
|
+
- Fix annotation placement in nested files and around trailing user comments [\#334](https://github.com/drwl/annotaterb/pull/334) ([yamat47](https://github.com/yamat47))
|
|
37
|
+
- Preserve indentation when re-running annotaterb on nested-position annotations. [\#333](https://github.com/drwl/annotaterb/pull/333) ([yamat47](https://github.com/yamat47))
|
|
38
|
+
- Respect `--config-path` during config loading [\#331](https://github.com/drwl/annotaterb/pull/331) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
39
|
+
- Fall back to current task when no top-level task is set [\#328](https://github.com/drwl/annotaterb/pull/328) ([jordan-brough](https://github.com/jordan-brough))
|
|
40
|
+
- Fix --show-migration option not working on Rails 7.2+. [\#327](https://github.com/drwl/annotaterb/pull/327) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
41
|
+
- Exit with non-zero code when database is not accessible [\#326](https://github.com/drwl/annotaterb/pull/326) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
42
|
+
- fix potential ReDoS in route annotator regex [\#325](https://github.com/drwl/annotaterb/pull/325) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
43
|
+
- Support opt-in display of PostgreSQL enum types in annotations [\#324](https://github.com/drwl/annotaterb/pull/324) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
44
|
+
- Fix error when column comments contain `%` [\#322](https://github.com/drwl/annotaterb/pull/322) ([willnet](https://github.com/willnet))
|
|
45
|
+
- Add unit tests for AnnotateRb::Helper [\#321](https://github.com/drwl/annotaterb/pull/321) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
46
|
+
- Migrate issue templates to folder-based format [\#320](https://github.com/drwl/annotaterb/pull/320) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
47
|
+
- Add opt-in show\_indexes\_comments option to display index comments [\#319](https://github.com/drwl/annotaterb/pull/319) ([olleolleolle](https://github.com/olleolleolle))
|
|
48
|
+
- Only connect SecondaryRecord to secondary DB when MULTI\_DB\_TEST is set [\#318](https://github.com/drwl/annotaterb/pull/318) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
49
|
+
- Correct secondary database setup in dummy app [\#317](https://github.com/drwl/annotaterb/pull/317) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
50
|
+
- Remove duplicate ignore\_unknown\_models option definition [\#316](https://github.com/drwl/annotaterb/pull/316) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
51
|
+
- Fix column annotation alignment for CJK and fullwidth characters [\#315](https://github.com/drwl/annotaterb/pull/315) ([SergeyGildenshtern](https://github.com/SergeyGildenshtern))
|
|
52
|
+
- Added command example for applying annotations to README [\#313](https://github.com/drwl/annotaterb/pull/313) ([zackerms](https://github.com/zackerms))
|
|
53
|
+
- Generate changelog for v4.22.0 [\#311](https://github.com/drwl/annotaterb/pull/311) ([drwl](https://github.com/drwl))
|
|
54
|
+
- Add opt-in support for automatically annotating routes after migration tasks [\#293](https://github.com/drwl/annotaterb/pull/293) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
55
|
+
|
|
56
|
+
## [v4.22.0](https://github.com/drwl/annotaterb/tree/v4.22.0) (2026-02-12)
|
|
57
|
+
|
|
58
|
+
[Full Changelog](https://github.com/drwl/annotaterb/compare/v4.21.0...v4.22.0)
|
|
59
|
+
|
|
60
|
+
**Implemented enhancements:**
|
|
61
|
+
|
|
62
|
+
- Feature: ruby-lsp addon [\#175](https://github.com/drwl/annotaterb/issues/175)
|
|
63
|
+
- Mounting ActionCable leads to weird annotation [\#161](https://github.com/drwl/annotaterb/issues/161)
|
|
64
|
+
|
|
65
|
+
**Fixed bugs:**
|
|
66
|
+
|
|
67
|
+
- Yardoc formatting for comments on database attributes [\#162](https://github.com/drwl/annotaterb/issues/162)
|
|
68
|
+
|
|
69
|
+
**Closed issues:**
|
|
70
|
+
|
|
71
|
+
- New `ignore_multi_database_name` option seems to be non-functional [\#303](https://github.com/drwl/annotaterb/issues/303)
|
|
72
|
+
- Changing sort options does not change annotations [\#294](https://github.com/drwl/annotaterb/issues/294)
|
|
73
|
+
- CLI script for annotaterb not installed or runnable [\#290](https://github.com/drwl/annotaterb/issues/290)
|
|
74
|
+
|
|
75
|
+
**Merged pull requests:**
|
|
76
|
+
|
|
77
|
+
- Bump version to v4.22.0 [\#310](https://github.com/drwl/annotaterb/pull/310) ([drwl](https://github.com/drwl))
|
|
78
|
+
- Run CI on CRuby 4.0 [\#308](https://github.com/drwl/annotaterb/pull/308) ([viralpraxis](https://github.com/viralpraxis))
|
|
79
|
+
- Generate changelog for v4.21.0 [\#307](https://github.com/drwl/annotaterb/pull/307) ([drwl](https://github.com/drwl))
|
|
80
|
+
- fix NoMethodError when using nested\_position with fixture files [\#298](https://github.com/drwl/annotaterb/pull/298) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
81
|
+
- fix: Respect configured sort [\#295](https://github.com/drwl/annotaterb/pull/295) ([patrickarnett](https://github.com/patrickarnett))
|
|
82
|
+
- Use `#lease_connection` if available [\#292](https://github.com/drwl/annotaterb/pull/292) ([viralpraxis](https://github.com/viralpraxis))
|
|
83
|
+
- refactor: simplify primary key check logic \(no functional changes\) [\#285](https://github.com/drwl/annotaterb/pull/285) ([OdenTakashi](https://github.com/OdenTakashi))
|
|
84
|
+
- Honor skip\_on\_db\_migrate config option when runnig migrate tasks [\#274](https://github.com/drwl/annotaterb/pull/274) ([martinechtner](https://github.com/martinechtner))
|
|
85
|
+
|
|
3
86
|
## [v4.21.0](https://github.com/drwl/annotaterb/tree/v4.21.0) (2026-01-30)
|
|
4
87
|
|
|
5
88
|
[Full Changelog](https://github.com/drwl/annotaterb/compare/v4.20.0...v4.21.0)
|
data/README.md
CHANGED
|
@@ -64,6 +64,13 @@ $ bin/rails g annotate_rb:install
|
|
|
64
64
|
|
|
65
65
|
This will copy a rake task into your Rails project's `lib/tasks` directory that will hook into the Rails project rake tasks, automatically running AnnotateRb after database migration rake tasks.
|
|
66
66
|
|
|
67
|
+
```sh
|
|
68
|
+
$ bin/rails db:migrate
|
|
69
|
+
# ...
|
|
70
|
+
# Annotating models
|
|
71
|
+
# Annotated (1): app/models/task.rb
|
|
72
|
+
```
|
|
73
|
+
|
|
67
74
|
To skip the automatic annotation that happens after a db task, pass the environment variable `ANNOTATERB_SKIP_ON_DB_TASKS=1` before your command.
|
|
68
75
|
|
|
69
76
|
```sh
|
|
@@ -184,10 +191,10 @@ Additional options that work for annotating models and routes
|
|
|
184
191
|
-f [bare|rdoc|yard|markdown], Render Schema Information as plain/RDoc/Yard/Markdown
|
|
185
192
|
--format
|
|
186
193
|
--config_path [path] Path to configuration file (by default, .annotaterb.yml in the root of the project)
|
|
187
|
-
-p [before|top|after|bottom],
|
|
188
|
-
--position
|
|
189
|
-
--pc, --position-in-class [before|top|after|bottom]
|
|
190
|
-
Place the annotations at the top (before) or the
|
|
194
|
+
-p [before|top|after|bottom|before_doc],
|
|
195
|
+
--position Place the annotations at the top (before), bottom (after), or above the class documentation (before_doc) of the model/test/fixture/factory/route/serializer file(s)
|
|
196
|
+
--pc, --position-in-class [before|top|after|bottom|before_doc]
|
|
197
|
+
Place the annotations at the top (before), bottom (after), or above the class documentation (before_doc) of the model file
|
|
191
198
|
--pf, --position-in-factory [before|top|after|bottom]
|
|
192
199
|
Place the annotations at the top (before) or the bottom (after) of any factory files
|
|
193
200
|
--px, --position-in-fixture [before|top|after|bottom]
|
|
@@ -230,6 +237,151 @@ Annotaterb reads first the configuration file, if it exists, passes its content
|
|
|
230
237
|
|
|
231
238
|
For further details visit the [section in the migration guide](MIGRATION_GUIDE.md#automatic-annotations-after-running-database-migration-commands).
|
|
232
239
|
|
|
240
|
+
### Configuration options
|
|
241
|
+
|
|
242
|
+
Keys use snake_case and match the gem defaults in `AnnotateRb::Options`. CLI flags override values from the config file.
|
|
243
|
+
|
|
244
|
+
#### Position
|
|
245
|
+
|
|
246
|
+
| Option | Default | Description |
|
|
247
|
+
| --- | --- | --- |
|
|
248
|
+
| `position` | `before` | Fallback position for all `position_in_*` options. One of `before`, `top`, `after`, `bottom`, `before_doc`. |
|
|
249
|
+
| `position_in_class` | `before` | Position in model files. `before_doc` keeps class documentation adjacent to the class. |
|
|
250
|
+
| `position_in_factory` | `before` | Position in FactoryBot factory files. |
|
|
251
|
+
| `position_in_fixture` | `before` | Position in fixture files. |
|
|
252
|
+
| `position_in_test` | `before` | Position in test/spec files. |
|
|
253
|
+
| `position_in_routes` | `before` | Position in `config/routes.rb`. |
|
|
254
|
+
| `position_in_serializer` | `before` | Position in serializer files. |
|
|
255
|
+
| `position_in_additional_file_patterns` | `before` | Position in files matched by `additional_file_patterns`. |
|
|
256
|
+
| `nested_position` | `false` | Place annotations directly above nested classes/modules instead of at the top of the file. |
|
|
257
|
+
|
|
258
|
+
#### Schema annotation content
|
|
259
|
+
|
|
260
|
+
| Option | Default | Description |
|
|
261
|
+
| --- | --- | --- |
|
|
262
|
+
| `show_foreign_keys` | `true` | List foreign key constraints. |
|
|
263
|
+
| `show_complete_foreign_keys` | `false` | Use complete foreign key names. |
|
|
264
|
+
| `show_indexes` | `true` | List table indexes. |
|
|
265
|
+
| `show_indexes_comments` | `false` | Include index comments. |
|
|
266
|
+
| `show_indexes_include` | `false` | Include `INCLUDE` columns on indexes. |
|
|
267
|
+
| `simple_indexes` | `false` | Concat related indexes onto each column line. |
|
|
268
|
+
| `show_check_constraints` | `false` | List check constraints. |
|
|
269
|
+
| `show_enums` | `false` | Show PostgreSQL enum types. |
|
|
270
|
+
| `show_virtual_columns` | `false` | Show virtual/generated columns. |
|
|
271
|
+
| `include_version` | `false` | Include the migration version number. |
|
|
272
|
+
| `with_comment` | `true` | Include database comments (fallback for column/table comment flags). |
|
|
273
|
+
| `with_column_comments` | `true` | Include column comments. |
|
|
274
|
+
| `with_table_comments` | `true` | Include table comments. |
|
|
275
|
+
| `position_of_column_comment` | `with_name` | Column comment placement: `with_name` or `rightmost_column`. |
|
|
276
|
+
| `hide_default_column_types` | `""` | Comma-separated column types that omit defaults (e.g. `json,jsonb,hstore`). |
|
|
277
|
+
| `hide_limit_column_types` | `""` | Comma-separated column types that omit limits (e.g. `integer,boolean,text`). |
|
|
278
|
+
| `ignore_columns` | `null` | Regex of column names to skip. |
|
|
279
|
+
| `ignore_database_name` | `false` | Omit the database name from annotations. |
|
|
280
|
+
| `ignore_multi_database_name` | `false` | Omit the database name in multi-database setups. |
|
|
281
|
+
| `timestamp_columns` | `[created_at, updated_at]` | Column names treated as timestamps for classified sorting. |
|
|
282
|
+
| `classes_default_to_s` | `[]` | Class names whose default values are rendered with `to_s`. |
|
|
283
|
+
|
|
284
|
+
#### Sorting and format
|
|
285
|
+
|
|
286
|
+
| Option | Default | Description |
|
|
287
|
+
| --- | --- | --- |
|
|
288
|
+
| `sort` | `false` | Sort columns alphabetically. |
|
|
289
|
+
| `classified_sort` | `true` | Sort as id → other columns → timestamps → associations. |
|
|
290
|
+
| `grouped_polymorphic` | `false` | Group polymorphic associations when using `classified_sort`. |
|
|
291
|
+
| `format_markdown` | `false` | Render annotations as Markdown. |
|
|
292
|
+
| `format_rdoc` | `false` | Render annotations as RDoc. |
|
|
293
|
+
| `format_yard` | `false` | Render annotations as YARD. |
|
|
294
|
+
|
|
295
|
+
#### What to annotate / exclude
|
|
296
|
+
|
|
297
|
+
| Option | Default | Description |
|
|
298
|
+
| --- | --- | --- |
|
|
299
|
+
| `active_admin` | `false` | Annotate ActiveAdmin models. |
|
|
300
|
+
| `exclude_controllers` | `true` | Skip controller files. |
|
|
301
|
+
| `exclude_factories` | `false` | Skip factory files. |
|
|
302
|
+
| `exclude_fixtures` | `false` | Skip fixture files. |
|
|
303
|
+
| `exclude_helpers` | `true` | Skip helper files. |
|
|
304
|
+
| `exclude_scaffolds` | `true` | Skip scaffold files. |
|
|
305
|
+
| `exclude_serializers` | `false` | Skip serializer files. |
|
|
306
|
+
| `exclude_sti_subclasses` | `false` | Skip STI subclasses. |
|
|
307
|
+
| `exclude_tests` | `false` | Skip test/spec files. Can also be an array of symbols. |
|
|
308
|
+
| `ignore_model_sub_dir` | `false` | Ignore subdirectories under `model_dir`. |
|
|
309
|
+
| `ignore_unknown_models` | `false` | Suppress warnings for bad model files. |
|
|
310
|
+
|
|
311
|
+
#### Paths
|
|
312
|
+
|
|
313
|
+
| Option | Default | Description |
|
|
314
|
+
| --- | --- | --- |
|
|
315
|
+
| `model_dir` | `[app/models]` | Directories containing models. |
|
|
316
|
+
| `root_dir` | `[""]` | Root directories for multi-project layouts. |
|
|
317
|
+
| `additional_file_patterns` | `[]` | Extra paths/globs to annotate (supports `%MODEL_NAME%`). |
|
|
318
|
+
| `require` | `[]` | Extra files to require before loading models. |
|
|
319
|
+
|
|
320
|
+
#### Routes
|
|
321
|
+
|
|
322
|
+
| Option | Default | Description |
|
|
323
|
+
| --- | --- | --- |
|
|
324
|
+
| `ignore_routes` | `null` | Regex of routes to skip. |
|
|
325
|
+
| `timestamp` | `false` | Include a timestamp in route annotations. |
|
|
326
|
+
| `auto_annotate_routes_after_migrate` | `false` | Also annotate routes after DB migrate tasks. |
|
|
327
|
+
|
|
328
|
+
#### Wrappers and behavior
|
|
329
|
+
|
|
330
|
+
| Option | Default | Description |
|
|
331
|
+
| --- | --- | --- |
|
|
332
|
+
| `wrapper` | `null` | Text used for both opening and closing wrappers. |
|
|
333
|
+
| `wrapper_open` | `null` | Opening wrapper text (falls back to `wrapper`). |
|
|
334
|
+
| `wrapper_close` | `null` | Closing wrapper text (falls back to `wrapper`). |
|
|
335
|
+
| `force` | `false` | Rewrite annotations even when unchanged. |
|
|
336
|
+
| `frozen` | `false` | Exit non-zero if annotations would change. |
|
|
337
|
+
| `skip_on_db_migrate` | `false` | Skip automatic annotation after DB migrate tasks. |
|
|
338
|
+
| `debug` | `false` | Print resolved options for debugging. |
|
|
339
|
+
| `trace` | `false` | Print full stack traces when annotation fails. |
|
|
340
|
+
|
|
341
|
+
Example:
|
|
342
|
+
|
|
343
|
+
```yml
|
|
344
|
+
# .annotaterb.yml
|
|
345
|
+
position: after
|
|
346
|
+
show_foreign_keys: true
|
|
347
|
+
show_indexes: true
|
|
348
|
+
show_enums: true
|
|
349
|
+
classified_sort: true
|
|
350
|
+
model_dir:
|
|
351
|
+
- app/models
|
|
352
|
+
- app/models/concerns
|
|
353
|
+
skip_on_db_migrate: false
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Preserving class documentation comments
|
|
357
|
+
|
|
358
|
+
By default, when `position_in_class` is `before` (or `top`), AnnotateRb places the schema annotation immediately before the class declaration line. Any human-written documentation comment that was directly above the class is therefore pushed above the annotation.
|
|
359
|
+
|
|
360
|
+
If you prefer to keep the documentation comment adjacent to the class, set `position_in_class` to `before_doc`. The schema annotation is then inserted above the documentation comment block, leaving the comment directly before the class.
|
|
361
|
+
|
|
362
|
+
```ruby
|
|
363
|
+
# Source file:
|
|
364
|
+
# Doc about User
|
|
365
|
+
class User < ApplicationRecord
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# With position_in_class: before (default)
|
|
369
|
+
# Doc about User
|
|
370
|
+
# == Schema Information
|
|
371
|
+
# ...
|
|
372
|
+
class User < ApplicationRecord
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# With position_in_class: before_doc
|
|
376
|
+
# == Schema Information
|
|
377
|
+
# ...
|
|
378
|
+
# Doc about User
|
|
379
|
+
class User < ApplicationRecord
|
|
380
|
+
end
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
A "documentation comment" is the contiguous comment block immediately above the class declaration, with no blank line between the comments and the class. Recognized magic comments (`encoding`, `frozen_string_literal`, `shareable_constant_value`, `warn_indent`, `typed`, `rbs_inline`, plus Emacs/Vim style modelines) are excluded so the annotation can still be inserted between magic comments and the class doc.
|
|
384
|
+
|
|
233
385
|
### How to skip annotating a particular model
|
|
234
386
|
|
|
235
387
|
If you want to always skip annotations on a particular model, add this string
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.24.0
|
data/lib/annotate_rb/helper.rb
CHANGED
|
@@ -2,15 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
module AnnotateRb
|
|
4
4
|
module Helper
|
|
5
|
+
# Unicode ranges that typically occupy 2 columns in monospace terminals.
|
|
6
|
+
# This is a simplified wcwidth implementation.
|
|
7
|
+
DOUBLE_WIDTH_RANGES = [
|
|
8
|
+
0x1100..0x115F, # Hangul Jamo
|
|
9
|
+
0x2E80..0x303E, # CJK Radicals, Kangxi, CJK Symbols
|
|
10
|
+
0x3040..0x4DBF, # Hiragana, Katakana, Bopomofo, CJK Ext A
|
|
11
|
+
0x4E00..0x9FFF, # CJK Unified Ideographs
|
|
12
|
+
0xAC00..0xD7AF, # Hangul Syllables
|
|
13
|
+
0xF900..0xFAFF, # CJK Compatibility Ideographs
|
|
14
|
+
0xFE30..0xFE6F, # CJK Compatibility Forms
|
|
15
|
+
0xFF01..0xFF60, # Fullwidth Forms
|
|
16
|
+
0xFFE0..0xFFE6, # Fullwidth Signs
|
|
17
|
+
0x20000..0x2FA1F, # CJK Extensions B-F
|
|
18
|
+
0x30000..0x3134F # CJK Extensions G-I
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
5
21
|
class << self
|
|
6
22
|
def width(string)
|
|
7
|
-
string.
|
|
23
|
+
string.each_char.sum { |char| double_width?(char) ? 2 : 1 }
|
|
8
24
|
end
|
|
9
25
|
|
|
10
26
|
# TODO: Find another implementation that doesn't depend on ActiveSupport
|
|
11
27
|
def fallback(*args)
|
|
12
28
|
args.compact.detect(&:present?)
|
|
13
29
|
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def double_width?(char)
|
|
34
|
+
cp = char.ord
|
|
35
|
+
DOUBLE_WIDTH_RANGES.any? { |range| range.cover?(cp) }
|
|
36
|
+
end
|
|
14
37
|
end
|
|
15
38
|
end
|
|
16
39
|
end
|
|
@@ -5,7 +5,7 @@ module AnnotateRb
|
|
|
5
5
|
module AnnotatedFile
|
|
6
6
|
# Generates the file with fresh annotations
|
|
7
7
|
class Generator
|
|
8
|
-
def initialize(file_content, new_annotations, annotation_position, parser_klass, parsed_file, options)
|
|
8
|
+
def initialize(file_content, new_annotations, annotation_position, parser_klass, parsed_file, options, model_class_name: nil)
|
|
9
9
|
@annotation_position = annotation_position
|
|
10
10
|
@options = options
|
|
11
11
|
|
|
@@ -16,6 +16,7 @@ module AnnotateRb
|
|
|
16
16
|
|
|
17
17
|
@parser = parser_klass
|
|
18
18
|
@parsed_file = parsed_file
|
|
19
|
+
@model_class_name = model_class_name
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
# @return [String] Returns the annotated file content to be written back to a file
|
|
@@ -35,7 +36,7 @@ module AnnotateRb
|
|
|
35
36
|
# We need to get class start and class end depending on the position
|
|
36
37
|
parsed = @parser.parse(content_without_annotations)
|
|
37
38
|
|
|
38
|
-
_content = if
|
|
39
|
+
_content = if Parser::AFTER_POSITIONS.include?(annotation_write_position)
|
|
39
40
|
content_annotated_after(parsed, content_without_annotations)
|
|
40
41
|
else
|
|
41
42
|
content_annotated_before(parsed, content_without_annotations, annotation_write_position)
|
|
@@ -45,10 +46,14 @@ module AnnotateRb
|
|
|
45
46
|
private
|
|
46
47
|
|
|
47
48
|
def content_annotated_before(parsed, content_without_annotations, write_position)
|
|
48
|
-
same_write_position =
|
|
49
|
+
same_write_position = same_side?(write_position)
|
|
49
50
|
|
|
50
51
|
_constant_name, line_number_before = determine_annotation_position(parsed)
|
|
51
52
|
|
|
53
|
+
if Parser::DOC_AWARE_POSITIONS.include?(write_position)
|
|
54
|
+
line_number_before = class_doc_start_line(content_without_annotations.lines, line_number_before)
|
|
55
|
+
end
|
|
56
|
+
|
|
52
57
|
content_with_annotations_written_before = []
|
|
53
58
|
content_with_annotations_written_before << content_without_annotations.lines[0...line_number_before]
|
|
54
59
|
content_with_annotations_written_before << $/ if @parsed_file.has_leading_whitespace? && same_write_position
|
|
@@ -59,17 +64,34 @@ module AnnotateRb
|
|
|
59
64
|
content_with_annotations_written_before.join
|
|
60
65
|
end
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
def same_side?(write_position)
|
|
68
|
+
return false unless @parsed_file.has_annotations?
|
|
69
|
+
|
|
70
|
+
new_side = Parser::AFTER_POSITIONS.include?(write_position) ? :after : :before
|
|
71
|
+
@parsed_file.annotation_position == new_side
|
|
72
|
+
end
|
|
68
73
|
|
|
69
|
-
|
|
74
|
+
# Magic comments are excluded so annotations slot between them and
|
|
75
|
+
# the class doc rather than being treated as part of the doc.
|
|
76
|
+
def class_doc_start_line(content_lines, line_number_before)
|
|
77
|
+
doc_start = line_number_before
|
|
78
|
+
cursor = line_number_before - 1
|
|
70
79
|
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
while cursor >= 0
|
|
81
|
+
line = content_lines[cursor]
|
|
82
|
+
break if line.match?(/\A\s*\z/)
|
|
83
|
+
break unless line.match?(/\A\s*#/)
|
|
84
|
+
break if FileParser::MagicComment.match?(line)
|
|
85
|
+
|
|
86
|
+
doc_start = cursor
|
|
87
|
+
cursor -= 1
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
doc_start
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def determine_annotation_position(parsed)
|
|
94
|
+
FileParser::AnnotationTarget.find(parsed, @options, model_class_name: @model_class_name) || [nil, 0]
|
|
73
95
|
end
|
|
74
96
|
|
|
75
97
|
# Formats annotations with appropriate indentation for consistent code structure.
|
|
@@ -18,13 +18,26 @@ module AnnotateRb
|
|
|
18
18
|
def update
|
|
19
19
|
return "" if !@parsed_file.has_annotations?
|
|
20
20
|
|
|
21
|
-
new_annotation = wrapped_content(@new_annotations)
|
|
21
|
+
new_annotation = indent(wrapped_content(@new_annotations), existing_indentation)
|
|
22
22
|
|
|
23
23
|
_content = @file_content.sub(@parsed_file.annotations) { new_annotation }
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
private
|
|
27
27
|
|
|
28
|
+
# Returns the leading whitespace of the existing annotation block so
|
|
29
|
+
# nested-position annotations keep their indentation across re-runs.
|
|
30
|
+
def existing_indentation
|
|
31
|
+
first_line = @parsed_file.annotations.lines.first
|
|
32
|
+
first_line&.match(/\A([ \t]*)/)&.[](1) || ""
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def indent(content, indentation)
|
|
36
|
+
return content if indentation.empty?
|
|
37
|
+
|
|
38
|
+
content.lines.map { |line| (line == "\n") ? line : "#{indentation}#{line}" }.join
|
|
39
|
+
end
|
|
40
|
+
|
|
28
41
|
def wrapped_content(content)
|
|
29
42
|
wrapper_open = if @options[:wrapper_open]
|
|
30
43
|
"# #{@options[:wrapper_open]}\n"
|
|
@@ -27,6 +27,9 @@ module AnnotateRb
|
|
|
27
27
|
IndexAnnotation::AnnotationBuilder.new(@model, @options).build,
|
|
28
28
|
ForeignKeyAnnotation::AnnotationBuilder.new(@model, @options).build,
|
|
29
29
|
CheckConstraintAnnotation::AnnotationBuilder.new(@model, @options).build,
|
|
30
|
+
UniqueConstraintAnnotation::AnnotationBuilder.new(@model, @options).build,
|
|
31
|
+
ExclusionConstraintAnnotation::AnnotationBuilder.new(@model, @options).build,
|
|
32
|
+
EnumAnnotation::AnnotationBuilder.new(@model, @options).build,
|
|
30
33
|
SchemaFooter.new
|
|
31
34
|
]
|
|
32
35
|
end
|
|
@@ -64,7 +67,7 @@ module AnnotateRb
|
|
|
64
67
|
table_name = @model.table_name
|
|
65
68
|
table_comment = @model.connection.try(:table_comment, @model.table_name)
|
|
66
69
|
max_size = @model.max_schema_info_width
|
|
67
|
-
database_name = @model.database_name if
|
|
70
|
+
database_name = @model.database_name if show_database_name?
|
|
68
71
|
|
|
69
72
|
_annotation = Annotation.new(@options,
|
|
70
73
|
version: version, table_name: table_name, table_comment: table_comment,
|
|
@@ -73,6 +76,11 @@ module AnnotateRb
|
|
|
73
76
|
|
|
74
77
|
private
|
|
75
78
|
|
|
79
|
+
# TODO: Consolidate ignore_database_name and ignore_multi_database_name; they serve the same purpose
|
|
80
|
+
def show_database_name?
|
|
81
|
+
!@options[:ignore_database_name] && multi_db_environment?
|
|
82
|
+
end
|
|
83
|
+
|
|
76
84
|
def multi_db_environment?
|
|
77
85
|
return false if @options[:ignore_multi_database_name]
|
|
78
86
|
|
|
@@ -33,6 +33,9 @@ module AnnotateRb
|
|
|
33
33
|
warn "Unable to process #{@file}: #{e.message}"
|
|
34
34
|
warn "\t#{e.backtrace.join("\n\t")}" if @options[:trace]
|
|
35
35
|
end
|
|
36
|
+
rescue ActiveRecord::ConnectionNotEstablished,
|
|
37
|
+
ActiveRecord::NoDatabaseError => e
|
|
38
|
+
abort "AnnotateRb: Database connection error - #{e.message}"
|
|
36
39
|
rescue => e
|
|
37
40
|
warn "Unable to process #{@file}: #{e.message}"
|
|
38
41
|
warn "\t#{e.backtrace.join("\n\t")}" if @options[:trace]
|
|
@@ -4,14 +4,17 @@ module AnnotateRb
|
|
|
4
4
|
module ModelAnnotator
|
|
5
5
|
# Compares the current file content and new annotation block and generates the column annotation differences
|
|
6
6
|
class AnnotationDiffGenerator
|
|
7
|
-
|
|
7
|
+
# Leading whitespace is tolerated so that annotations placed inside a
|
|
8
|
+
# nested module/class (i.e. with --nested-position, or hand-aligned)
|
|
9
|
+
# still match.
|
|
10
|
+
HEADER_PATTERN = /(^[ \t]*# Table name:.*?\n([ \t]*#.*\r?\n)*\r?)/
|
|
8
11
|
# Example matches:
|
|
9
12
|
# - "# id :uuid not null, primary key"
|
|
10
13
|
# - "# title(length 255) :string not null"
|
|
11
14
|
# - "# status(a/b/c) :string not null"
|
|
12
15
|
# - "# created_at :datetime not null"
|
|
13
16
|
# - "# updated_at :datetime not null"
|
|
14
|
-
COLUMN_PATTERN =
|
|
17
|
+
COLUMN_PATTERN = /^[ \t]*#[\t ]+[[\p{L}\p{N}_]*.`\[\]():]+(?:\(.*?\))?[\t ]+.+$/
|
|
15
18
|
class << self
|
|
16
19
|
def call(file_content, annotation_block)
|
|
17
20
|
new(file_content, annotation_block).generate
|
|
@@ -30,14 +33,16 @@ module AnnotateRb
|
|
|
30
33
|
current_annotations = @file_content.match(HEADER_PATTERN).to_s
|
|
31
34
|
new_annotations = @annotation_block.match(HEADER_PATTERN).to_s
|
|
32
35
|
|
|
36
|
+
# Strip leading whitespace from each scanned column so that an
|
|
37
|
+
# indented existing block compares equal to a flush-left new block.
|
|
33
38
|
current_annotation_columns = if current_annotations.present?
|
|
34
|
-
current_annotations.scan(COLUMN_PATTERN)
|
|
39
|
+
current_annotations.scan(COLUMN_PATTERN).map(&:lstrip)
|
|
35
40
|
else
|
|
36
41
|
[]
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
new_annotation_columns = if new_annotations.present?
|
|
40
|
-
new_annotations.scan(COLUMN_PATTERN)
|
|
45
|
+
new_annotations.scan(COLUMN_PATTERN).map(&:lstrip)
|
|
41
46
|
else
|
|
42
47
|
[]
|
|
43
48
|
end
|
|
@@ -84,6 +84,13 @@ module AnnotateRb
|
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
if column_type == "enum" && @options[:show_enums]
|
|
88
|
+
enum_type_name = @column.sql_type
|
|
89
|
+
if enum_type_name.present? && enum_type_name != "enum"
|
|
90
|
+
attrs << "enum_type: #{enum_type_name}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
87
94
|
# Check if the column is a virtual column and print the function
|
|
88
95
|
if @options[:show_virtual_columns] && @column.virtual?
|
|
89
96
|
# Any whitespace in the function gets reduced to a single space
|
|
@@ -42,7 +42,7 @@ module AnnotateRb
|
|
|
42
42
|
|
|
43
43
|
def to_yard
|
|
44
44
|
res = ""
|
|
45
|
-
res +=
|
|
45
|
+
res += "# @!attribute #{name}\n"
|
|
46
46
|
|
|
47
47
|
ruby_class = if @column.respond_to?(:array) && @column.array
|
|
48
48
|
"Array<#{map_col_type_to_ruby_classes(type)}>"
|
|
@@ -50,7 +50,7 @@ module AnnotateRb
|
|
|
50
50
|
map_col_type_to_ruby_classes(type)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
res +=
|
|
53
|
+
res += "# @return [#{ruby_class}]"
|
|
54
54
|
|
|
55
55
|
res
|
|
56
56
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AnnotateRb
|
|
4
|
+
module ModelAnnotator
|
|
5
|
+
module EnumAnnotation
|
|
6
|
+
class Annotation
|
|
7
|
+
HEADER_TEXT = "Enums"
|
|
8
|
+
|
|
9
|
+
def initialize(enums)
|
|
10
|
+
@enums = enums
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def body
|
|
14
|
+
[
|
|
15
|
+
Components::BlankCommentLine.new,
|
|
16
|
+
Components::Header.new(HEADER_TEXT),
|
|
17
|
+
Components::BlankCommentLine.new,
|
|
18
|
+
*@enums
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_markdown
|
|
23
|
+
body.map(&:to_markdown).join("\n")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_rdoc
|
|
27
|
+
body.map(&:to_rdoc).join("\n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_yard
|
|
31
|
+
body.map(&:to_yard).join("\n")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_default
|
|
35
|
+
body.map(&:to_default).join("\n")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AnnotateRb
|
|
4
|
+
module ModelAnnotator
|
|
5
|
+
module EnumAnnotation
|
|
6
|
+
class AnnotationBuilder
|
|
7
|
+
def initialize(model, options)
|
|
8
|
+
@model = model
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build
|
|
13
|
+
return Components::NilComponent.new unless @options[:show_enums]
|
|
14
|
+
|
|
15
|
+
enum_types = @model.enum_types
|
|
16
|
+
return Components::NilComponent.new if enum_types.empty?
|
|
17
|
+
|
|
18
|
+
# Filter to only enum types used by this table's columns
|
|
19
|
+
table_enum_types = @model.columns.select { |col| col.type == :enum }
|
|
20
|
+
.map { |col| col.sql_type.to_s }
|
|
21
|
+
.uniq
|
|
22
|
+
|
|
23
|
+
relevant_enums = enum_types
|
|
24
|
+
.filter_map { |name, values| [name.to_s, values] if table_enum_types.include?(name.to_s) }
|
|
25
|
+
return Components::NilComponent.new if relevant_enums.empty?
|
|
26
|
+
|
|
27
|
+
max_size = relevant_enums.map { |name, _values| name.size }.max + 1
|
|
28
|
+
|
|
29
|
+
components = relevant_enums.sort_by { |name, _values| name }.map do |name, values|
|
|
30
|
+
EnumComponent.new(name, values, max_size)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Annotation.new(components)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|