awesome_annotate 0.1.4 → 0.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/.rubocop.yml +36 -4
- data/.ruby-version +1 -0
- data/CHANGELOG.md +21 -0
- data/README.md +187 -12
- data/Rakefile +3 -3
- data/exe/awesome_annotate +2 -3
- data/lib/awesome_annotate/annotation_block.rb +47 -0
- data/lib/awesome_annotate/cli.rb +40 -3
- data/lib/awesome_annotate/error.rb +6 -0
- data/lib/awesome_annotate/model.rb +93 -22
- data/lib/awesome_annotate/rails_environment.rb +11 -0
- data/lib/awesome_annotate/route.rb +34 -13
- data/lib/awesome_annotate/schema_annotation.rb +83 -0
- data/lib/awesome_annotate/version.rb +1 -1
- data/lib/awesome_annotate.rb +3 -6
- metadata +28 -14
- data/awesome_annotate.gemspec +0 -36
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d120e2f6dba99ee06506353a609f7b68e4ad08213141762b77ca6dacacc854ce
|
|
4
|
+
data.tar.gz: 6bb8098703ec777bfc7dcc9351ca88ed4c2f9655cb122c71f25b398165aad187
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d04a22f78762b7fd4de71460b710b512b3c23e126ce136afa326a7aa1a32f7bc9be2c72ed7fa3a740da266ae2cea8883d0a156296089a1ed3dc8fa0b88ae02f7
|
|
7
|
+
data.tar.gz: d64c97ff823e7eb03e2f16a9e511343ef6dc9856c33e78b2e0003e8ccaf390ce7adc87dedb62a1299aa320b0293d9a148677fbc86b7d9e6886d8e3c210ee9a1a
|
data/.rubocop.yml
CHANGED
|
@@ -1,18 +1,50 @@
|
|
|
1
|
-
inherit_from:
|
|
2
|
-
- .rubocop_todo.yml
|
|
3
|
-
|
|
4
1
|
require:
|
|
5
2
|
- rubocop-rake
|
|
6
3
|
- rubocop-rspec
|
|
7
4
|
|
|
8
5
|
AllCops:
|
|
6
|
+
TargetRubyVersion: 3.0
|
|
9
7
|
Exclude:
|
|
10
8
|
- 'vendor/**/*'
|
|
11
9
|
- 'spec/fixtures/**/*'
|
|
12
10
|
- 'tmp/**/*'
|
|
13
|
-
- 'spec/integration/**/*'
|
|
14
11
|
NewCops: enable
|
|
15
12
|
|
|
13
|
+
Style/Documentation:
|
|
14
|
+
Enabled: false
|
|
15
|
+
|
|
16
16
|
Metrics/BlockLength:
|
|
17
17
|
Exclude:
|
|
18
18
|
- 'spec/**/*.rb'
|
|
19
|
+
|
|
20
|
+
Metrics/AbcSize:
|
|
21
|
+
Exclude:
|
|
22
|
+
- 'spec/support/**/*'
|
|
23
|
+
|
|
24
|
+
RSpec/VerifiedDoubles:
|
|
25
|
+
Exclude:
|
|
26
|
+
- 'spec/support/**/*'
|
|
27
|
+
|
|
28
|
+
RSpec/MultipleExpectations:
|
|
29
|
+
Exclude:
|
|
30
|
+
- 'spec/**/*'
|
|
31
|
+
|
|
32
|
+
RSpec/NestedGroups:
|
|
33
|
+
Exclude:
|
|
34
|
+
- 'spec/**/*'
|
|
35
|
+
|
|
36
|
+
RSpec/HooksBeforeExamples:
|
|
37
|
+
Exclude:
|
|
38
|
+
- 'spec/**/*'
|
|
39
|
+
|
|
40
|
+
RSpec/ExampleLength:
|
|
41
|
+
Exclude:
|
|
42
|
+
- 'spec/**/*'
|
|
43
|
+
|
|
44
|
+
RSpec/DescribeClass:
|
|
45
|
+
Exclude:
|
|
46
|
+
- 'spec/integration/**/*'
|
|
47
|
+
|
|
48
|
+
Lint/EmptyClass:
|
|
49
|
+
Exclude:
|
|
50
|
+
- 'spec/support/rails.rb'
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.2.2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-05-10
|
|
4
|
+
|
|
5
|
+
- Added model schema annotations with column type, nullability, primary key,
|
|
6
|
+
default value, and basic index information.
|
|
7
|
+
- Added duplicate-safe annotation blocks with AwesomeAnnotate start and end
|
|
8
|
+
markers.
|
|
9
|
+
- Added route annotations for `config/routes.rb`.
|
|
10
|
+
- Added model commands:
|
|
11
|
+
- `awesome_annotate model MODEL`
|
|
12
|
+
- `awesome_annotate models`
|
|
13
|
+
- `awesome_annotate models MODEL...`
|
|
14
|
+
- Added `awesome_annotate all` to annotate all models and routes.
|
|
15
|
+
- Added remove commands:
|
|
16
|
+
- `awesome_annotate remove model MODEL`
|
|
17
|
+
- `awesome_annotate remove models`
|
|
18
|
+
- `awesome_annotate remove routes`
|
|
19
|
+
- `awesome_annotate remove all`
|
|
20
|
+
- Added Rails-like integration specs and GitHub Actions CI for RSpec and
|
|
21
|
+
RuboCop.
|
|
22
|
+
- Documented supported Ruby and Active Record versions.
|
|
23
|
+
|
|
3
24
|
## [0.1.3] - 2024-05-05
|
|
4
25
|
|
|
5
26
|
- Add a new feature
|
data/README.md
CHANGED
|
@@ -1,35 +1,210 @@
|
|
|
1
1
|
# AwesomeAnnotate
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AwesomeAnnotate adds generated schema and route comments to Rails applications.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This gem is intended as a small replacement-style tool for the basic features of
|
|
6
|
+
the original `annotate` gem:
|
|
7
|
+
|
|
8
|
+
- annotate model files with Active Record schema information
|
|
9
|
+
- annotate `config/routes.rb` with the application's route table
|
|
10
|
+
- replace previous AwesomeAnnotate blocks instead of appending duplicates
|
|
11
|
+
- remove generated AwesomeAnnotate blocks
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Ruby 3.0 or later, below 4.0
|
|
16
|
+
- Active Record 6.1 or later, below 8.0
|
|
17
|
+
- Rails application layout with `config/environment.rb`
|
|
18
|
+
|
|
19
|
+
Development currently uses the Ruby version in `.ruby-version`.
|
|
6
20
|
|
|
7
21
|
## Installation
|
|
8
22
|
|
|
9
|
-
|
|
23
|
+
Add this line to your Rails application's Gemfile:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem 'awesome_annotate'
|
|
27
|
+
```
|
|
10
28
|
|
|
11
|
-
|
|
29
|
+
Then run:
|
|
12
30
|
|
|
13
|
-
|
|
31
|
+
```sh
|
|
32
|
+
bundle install
|
|
33
|
+
```
|
|
14
34
|
|
|
15
|
-
If
|
|
35
|
+
If you are using this repository directly before publishing the gem, use a Git
|
|
36
|
+
source instead:
|
|
16
37
|
|
|
17
|
-
|
|
38
|
+
```ruby
|
|
39
|
+
gem 'awesome_annotate', git: 'https://github.com/wisdom-plus/awesome_annotate'
|
|
40
|
+
```
|
|
18
41
|
|
|
19
42
|
## Usage
|
|
20
43
|
|
|
21
|
-
|
|
44
|
+
Run commands from the root directory of a Rails application.
|
|
45
|
+
|
|
46
|
+
Available commands:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
bundle exec awesome_annotate model user
|
|
50
|
+
bundle exec awesome_annotate models
|
|
51
|
+
bundle exec awesome_annotate models user article admin/user
|
|
52
|
+
bundle exec awesome_annotate routes
|
|
53
|
+
bundle exec awesome_annotate all
|
|
54
|
+
bundle exec awesome_annotate remove model user
|
|
55
|
+
bundle exec awesome_annotate remove models
|
|
56
|
+
bundle exec awesome_annotate remove routes
|
|
57
|
+
bundle exec awesome_annotate remove all
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Annotate a model:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
bundle exec awesome_annotate model user
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Annotate all models:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
bundle exec awesome_annotate models
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Annotate specific models:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
bundle exec awesome_annotate models user article admin/user
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Annotate all models and routes:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
bundle exec awesome_annotate all
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Remove generated annotations:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
bundle exec awesome_annotate remove model user
|
|
88
|
+
bundle exec awesome_annotate remove models
|
|
89
|
+
bundle exec awesome_annotate remove routes
|
|
90
|
+
bundle exec awesome_annotate remove all
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This loads `config/environment.rb`, resolves `User`, reads its Active Record
|
|
94
|
+
columns, and writes a schema block before the class definition in
|
|
95
|
+
`app/models/user.rb`:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
# == AwesomeAnnotate: columns
|
|
99
|
+
# == Schema Information
|
|
100
|
+
#
|
|
101
|
+
# Table name: users
|
|
102
|
+
#
|
|
103
|
+
# id :integer not null, primary key
|
|
104
|
+
# name :string
|
|
105
|
+
# email :string not null, default("")
|
|
106
|
+
# created_at :datetime not null
|
|
107
|
+
# updated_at :datetime not null
|
|
108
|
+
#
|
|
109
|
+
# Indexes
|
|
110
|
+
#
|
|
111
|
+
# (email) UNIQUE, index_users_on_email
|
|
112
|
+
# (name,email) index_users_on_name_and_email
|
|
113
|
+
#
|
|
114
|
+
# == /AwesomeAnnotate: columns
|
|
115
|
+
class User < ApplicationRecord
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Annotate routes:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
bundle exec awesome_annotate routes
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This writes a generated route block before `Rails.application.routes.draw do` in
|
|
126
|
+
`config/routes.rb`:
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
# == AwesomeAnnotate: routes
|
|
130
|
+
# Prefix Verb URI Pattern Controller#Action
|
|
131
|
+
# users GET /users(.:format) users#index
|
|
132
|
+
# == /AwesomeAnnotate: routes
|
|
133
|
+
Rails.application.routes.draw do
|
|
134
|
+
end
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Print the gem version:
|
|
138
|
+
|
|
139
|
+
```sh
|
|
140
|
+
bundle exec awesome_annotate --version
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Short aliases are also available:
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
bundle exec awesome_annotate -m user
|
|
147
|
+
bundle exec awesome_annotate -r
|
|
148
|
+
bundle exec awesome_annotate -v
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Generated Blocks
|
|
152
|
+
|
|
153
|
+
AwesomeAnnotate wraps generated comments with start and end markers:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
# == AwesomeAnnotate: columns
|
|
157
|
+
# ...
|
|
158
|
+
# == /AwesomeAnnotate: columns
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
When the command is run again, the existing block with the same marker is
|
|
162
|
+
replaced. This prevents duplicate annotations from being appended on repeated
|
|
163
|
+
runs.
|
|
164
|
+
|
|
165
|
+
## Current Limitations
|
|
166
|
+
|
|
167
|
+
- Only model schema blocks and routes are supported.
|
|
168
|
+
- Model annotations include column type, nullability, primary key, and default
|
|
169
|
+
values, plus basic index information.
|
|
170
|
+
- Model annotation currently targets files under `app/models`.
|
|
171
|
+
- Model class detection expects a simple class declaration such as
|
|
172
|
+
`class User < ApplicationRecord`.
|
|
173
|
+
- Existing comments generated by other annotate tools are not migrated or
|
|
174
|
+
removed automatically.
|
|
175
|
+
- Ruby 4 is not currently supported.
|
|
22
176
|
|
|
23
177
|
## Development
|
|
24
178
|
|
|
25
|
-
|
|
179
|
+
Install dependencies:
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
bundle install
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Run the test suite:
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
bundle exec rspec
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Run RuboCop:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
bundle exec rubocop
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Build the gem locally:
|
|
26
198
|
|
|
27
|
-
|
|
199
|
+
```sh
|
|
200
|
+
bundle exec rake build
|
|
201
|
+
```
|
|
28
202
|
|
|
29
203
|
## Contributing
|
|
30
204
|
|
|
31
|
-
Bug reports and pull requests are welcome on GitHub at
|
|
205
|
+
Bug reports and pull requests are welcome on GitHub at
|
|
206
|
+
https://github.com/wisdom-plus/awesome_annotate.
|
|
32
207
|
|
|
33
208
|
## License
|
|
34
209
|
|
|
35
|
-
The gem is available as open source under the terms of the
|
|
210
|
+
The gem is available as open source under the terms of the MIT License.
|
data/Rakefile
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
5
|
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
|
7
7
|
|
|
8
|
-
require
|
|
8
|
+
require 'rubocop/rake_task'
|
|
9
9
|
|
|
10
10
|
RuboCop::RakeTask.new
|
|
11
11
|
|
data/exe/awesome_annotate
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
|
-
unless File.exist?('./Rakefile') || File.exist?('./Gemfile')
|
|
4
|
-
abort 'Please run annotate from the root of the project.'
|
|
5
|
-
end
|
|
4
|
+
abort 'Please run annotate from the root of the project.' unless File.exist?('./Rakefile') || File.exist?('./Gemfile')
|
|
6
5
|
require 'awesome_annotate'
|
|
7
6
|
|
|
8
7
|
AwesomeAnnotate::CLI.start ARGV
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AwesomeAnnotate
|
|
4
|
+
module AnnotationBlock
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def replace_or_insert_annotation(file_path:, marker:, content:, before:)
|
|
8
|
+
path = file_path.to_s
|
|
9
|
+
file_content = File.read(path)
|
|
10
|
+
annotation = annotation_block(marker, content)
|
|
11
|
+
|
|
12
|
+
File.write(path, replace_annotation(file_content, marker, annotation, before))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def remove_annotation(file_path:, marker:)
|
|
16
|
+
path = file_path.to_s
|
|
17
|
+
file_content = File.read(path)
|
|
18
|
+
pattern = annotation_block_pattern(marker)
|
|
19
|
+
|
|
20
|
+
return false unless file_content.match?(pattern)
|
|
21
|
+
|
|
22
|
+
File.write(path, file_content.gsub(pattern, ''))
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def annotation_block(marker, content)
|
|
27
|
+
body = content.end_with?("\n") ? content : "#{content}\n"
|
|
28
|
+
|
|
29
|
+
"# == AwesomeAnnotate: #{marker}\n" \
|
|
30
|
+
"#{body}" \
|
|
31
|
+
"# == /AwesomeAnnotate: #{marker}\n\n"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def annotation_block_pattern(marker)
|
|
35
|
+
escaped_marker = Regexp.escape(marker)
|
|
36
|
+
%r{^# == AwesomeAnnotate: #{escaped_marker}\n.*?^# == /AwesomeAnnotate: #{escaped_marker}\n(?:\n)*}m
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def replace_annotation(file_content, marker, annotation, before)
|
|
40
|
+
pattern = annotation_block_pattern(marker)
|
|
41
|
+
|
|
42
|
+
return file_content.sub(pattern, annotation) if file_content.match?(pattern)
|
|
43
|
+
|
|
44
|
+
file_content.sub(before, "#{annotation}\\0")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
data/lib/awesome_annotate/cli.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'awesome_annotate'
|
|
2
4
|
require 'awesome_annotate/model'
|
|
3
5
|
require 'awesome_annotate/route'
|
|
@@ -5,11 +7,34 @@ require 'awesome_annotate/version'
|
|
|
5
7
|
require 'thor'
|
|
6
8
|
|
|
7
9
|
module AwesomeAnnotate
|
|
10
|
+
class Remove < Thor
|
|
11
|
+
desc 'model [model_name]', 'remove annotation from a model'
|
|
12
|
+
def model(model_name)
|
|
13
|
+
AwesomeAnnotate::Model.new.remove(model_name)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc 'models [model_names...]', 'remove annotations from all models or specified models'
|
|
17
|
+
def models(*model_names)
|
|
18
|
+
AwesomeAnnotate::Model.new.remove_all(model_names)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc 'routes', 'remove annotation from `config/routes.rb`'
|
|
22
|
+
def routes
|
|
23
|
+
AwesomeAnnotate::Route.new.remove
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc 'all', 'remove annotations from all models and routes'
|
|
27
|
+
def all
|
|
28
|
+
AwesomeAnnotate::Model.new.remove_all
|
|
29
|
+
AwesomeAnnotate::Route.new.remove
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
8
33
|
class CLI < Thor
|
|
9
34
|
include Thor::Actions
|
|
10
35
|
|
|
11
36
|
map %w[--version -v] => :print_version
|
|
12
|
-
desc
|
|
37
|
+
desc '--version, -v', 'print the version'
|
|
13
38
|
def print_version
|
|
14
39
|
say AwesomeAnnotate::VERSION
|
|
15
40
|
end
|
|
@@ -20,13 +45,25 @@ module AwesomeAnnotate
|
|
|
20
45
|
AwesomeAnnotate::Model.new.annotate(model_name)
|
|
21
46
|
end
|
|
22
47
|
|
|
48
|
+
desc 'models [model_names...]', 'annotate all models or specified models'
|
|
49
|
+
def models(*model_names)
|
|
50
|
+
AwesomeAnnotate::Model.new.annotate_all(model_names)
|
|
51
|
+
end
|
|
52
|
+
|
|
23
53
|
map %w[routes -r] => :routes
|
|
24
|
-
desc 'routes',
|
|
54
|
+
desc 'routes', 'Writes application route information to `config/routes.rb`.'
|
|
25
55
|
def routes
|
|
26
56
|
AwesomeAnnotate::Route.new.annotate
|
|
27
57
|
end
|
|
28
58
|
|
|
29
|
-
|
|
59
|
+
desc 'all', 'annotate all models and routes'
|
|
60
|
+
def all
|
|
61
|
+
AwesomeAnnotate::Model.new.annotate_all
|
|
62
|
+
AwesomeAnnotate::Route.new.annotate
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
desc 'remove SUBCOMMAND', 'remove generated annotations'
|
|
66
|
+
subcommand 'remove', Remove
|
|
30
67
|
|
|
31
68
|
def self.exit_on_failure?
|
|
32
69
|
true
|
|
@@ -1,68 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_record'
|
|
2
4
|
require 'thor'
|
|
5
|
+
require_relative 'annotation_block'
|
|
6
|
+
require_relative 'error'
|
|
7
|
+
require_relative 'rails_environment'
|
|
8
|
+
require_relative 'schema_annotation'
|
|
3
9
|
|
|
4
10
|
module AwesomeAnnotate
|
|
5
11
|
class Model < Thor
|
|
12
|
+
include AnnotationBlock
|
|
6
13
|
include Thor::Actions
|
|
14
|
+
include RailsEnvironment
|
|
15
|
+
include SchemaAnnotation
|
|
16
|
+
|
|
17
|
+
def initialize(params = {})
|
|
18
|
+
super()
|
|
19
|
+
@env_file_path = Pathname.new(params[:env_file_path] || 'config/environment.rb')
|
|
20
|
+
@model_dir = Pathname.new(params[:model_dir] || 'app/models')
|
|
21
|
+
end
|
|
7
22
|
|
|
8
23
|
desc 'model [model name]', 'annotate your model'
|
|
9
24
|
def annotate(model_name)
|
|
10
|
-
|
|
25
|
+
raise 'Rails application path is required' unless @env_file_path.exist?
|
|
26
|
+
|
|
27
|
+
load_rails_environment
|
|
28
|
+
annotate_loaded_model(model_name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
desc 'models [model names]', 'annotate all models or specified models'
|
|
32
|
+
def annotate_all(model_names = [])
|
|
33
|
+
raise 'Rails application path is required' unless @env_file_path.exist?
|
|
34
|
+
|
|
35
|
+
load_rails_environment
|
|
36
|
+
|
|
37
|
+
if model_names.empty?
|
|
38
|
+
discover_model_names.each { |model_name| annotate_discovered_model(model_name) }
|
|
39
|
+
else
|
|
40
|
+
model_names.each { |model_name| annotate_loaded_model(model_name) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc 'remove [model name]', 'remove annotation from your model'
|
|
45
|
+
def remove(model_name)
|
|
46
|
+
file_path = model_file_path(model_name)
|
|
47
|
+
|
|
48
|
+
remove_model_annotation(file_path)
|
|
49
|
+
end
|
|
11
50
|
|
|
12
|
-
|
|
51
|
+
desc 'remove_all [model names]', 'remove annotations from all models or specified models'
|
|
52
|
+
def remove_all(model_names = [])
|
|
53
|
+
if model_names.empty?
|
|
54
|
+
discovered_model_file_paths.each { |file_path| remove_model_annotation(file_path, report_missing: false) }
|
|
55
|
+
else
|
|
56
|
+
model_names.each { |model_name| remove(model_name) }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
13
59
|
|
|
14
|
-
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def annotate_loaded_model(model_name, report_missing: true)
|
|
63
|
+
klass = klass_name(model_name, report_missing: report_missing)
|
|
15
64
|
|
|
16
65
|
return say 'This model does not inherit activerecord' unless klass < ActiveRecord::Base
|
|
17
66
|
|
|
18
|
-
column_names = column_names(klass)
|
|
19
67
|
file_path = model_file_path(model_name)
|
|
20
68
|
|
|
21
|
-
insert_file_before_class(file_path, klass
|
|
69
|
+
insert_file_before_class(file_path, schema_annotation(klass))
|
|
22
70
|
|
|
23
71
|
say "annotate #{model_name.pluralize} table columns in #{file_path}"
|
|
24
72
|
end
|
|
25
73
|
|
|
26
|
-
|
|
74
|
+
def discover_model_names
|
|
75
|
+
discovered_model_file_paths.map { |file_path| model_name_from_file_path(file_path) }
|
|
76
|
+
end
|
|
27
77
|
|
|
28
|
-
def
|
|
29
|
-
|
|
78
|
+
def discovered_model_file_paths
|
|
79
|
+
Dir.glob(@model_dir.join('**/*.rb')).reject { |file_path| excluded_model_file?(file_path) }
|
|
30
80
|
end
|
|
31
81
|
|
|
32
|
-
def
|
|
33
|
-
Pathname.new('
|
|
82
|
+
def model_name_from_file_path(file_path)
|
|
83
|
+
Pathname.new(file_path).relative_path_from(@model_dir).sub_ext('').to_s
|
|
34
84
|
end
|
|
35
85
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
86
|
+
def excluded_model_file?(file_path)
|
|
87
|
+
relative_path = Pathname.new(file_path).relative_path_from(@model_dir).to_s
|
|
88
|
+
|
|
89
|
+
relative_path == 'application_record.rb' || relative_path.start_with?('concerns/')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def annotate_discovered_model(model_name)
|
|
93
|
+
annotate_loaded_model(model_name, report_missing: false)
|
|
94
|
+
rescue AwesomeAnnotate::NotFoundError
|
|
95
|
+
nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def remove_model_annotation(file_path, report_missing: true)
|
|
99
|
+
if remove_annotation(file_path: file_path, marker: 'columns')
|
|
100
|
+
say "remove model annotation in #{file_path}"
|
|
101
|
+
elsif report_missing
|
|
102
|
+
say "no model annotation in #{file_path}"
|
|
39
103
|
end
|
|
40
104
|
end
|
|
41
105
|
|
|
42
|
-
def
|
|
43
|
-
|
|
106
|
+
def insert_file_before_class(file_path, message)
|
|
107
|
+
replace_or_insert_annotation(
|
|
108
|
+
file_path: file_path,
|
|
109
|
+
marker: 'columns',
|
|
110
|
+
content: message,
|
|
111
|
+
before: /^class\s+\w+\s+<\s+\w+/
|
|
112
|
+
)
|
|
44
113
|
end
|
|
45
114
|
|
|
46
115
|
def model_file_path(model_name)
|
|
47
|
-
file_path = "#{model_dir}/#{model_name}.rb"
|
|
116
|
+
file_path = "#{@model_dir}/#{model_name}.rb"
|
|
48
117
|
|
|
49
118
|
unless File.exist?(file_path)
|
|
50
|
-
|
|
119
|
+
say 'Model file not found'
|
|
120
|
+
raise AwesomeAnnotate::NotFoundError
|
|
51
121
|
end
|
|
52
122
|
|
|
53
|
-
|
|
123
|
+
file_path
|
|
54
124
|
end
|
|
55
125
|
|
|
56
|
-
def klass_name(model_name)
|
|
126
|
+
def klass_name(model_name, report_missing: true)
|
|
57
127
|
name = model_name.singularize.camelize
|
|
58
|
-
|
|
59
|
-
|
|
128
|
+
Object.const_get(name)
|
|
60
129
|
rescue NameError
|
|
61
|
-
say
|
|
130
|
+
say 'Model not found' if report_missing
|
|
131
|
+
raise AwesomeAnnotate::NotFoundError
|
|
62
132
|
end
|
|
63
133
|
|
|
64
134
|
def self.source_root
|
|
65
135
|
Dir.pwd
|
|
66
136
|
end
|
|
137
|
+
private_class_method :source_root
|
|
67
138
|
end
|
|
68
139
|
end
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_record'
|
|
2
4
|
require 'thor'
|
|
5
|
+
require_relative 'annotation_block'
|
|
6
|
+
require_relative 'rails_environment'
|
|
3
7
|
|
|
4
8
|
module AwesomeAnnotate
|
|
5
9
|
class Route < Thor
|
|
10
|
+
include AnnotationBlock
|
|
6
11
|
include Thor::Actions
|
|
12
|
+
include RailsEnvironment
|
|
13
|
+
|
|
14
|
+
def initialize(params = {})
|
|
15
|
+
super()
|
|
16
|
+
@env_file_path = Pathname.new(params[:env_file_path] || 'config/environment.rb')
|
|
17
|
+
@route_file_path = Pathname.new(params[:route_file_path] || 'config/routes.rb')
|
|
18
|
+
end
|
|
7
19
|
|
|
8
20
|
desc 'annotate all routes', 'annotate your routes'
|
|
9
21
|
def annotate
|
|
10
|
-
|
|
22
|
+
raise 'Rails application path is required' unless @env_file_path.exist?
|
|
11
23
|
|
|
12
|
-
|
|
24
|
+
load_rails_environment
|
|
13
25
|
|
|
14
26
|
inspector = ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes)
|
|
15
27
|
formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet.new
|
|
@@ -17,20 +29,25 @@ module AwesomeAnnotate
|
|
|
17
29
|
routes = inspector.format(formatter, {})
|
|
18
30
|
route_message = parse_routes(routes)
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
raise 'Route file not found' unless @route_file_path.exist?
|
|
21
33
|
|
|
22
|
-
|
|
34
|
+
insert_file_before_class(@route_file_path, route_message)
|
|
35
|
+
|
|
36
|
+
say "annotate routes in #{@route_file_path}"
|
|
23
37
|
end
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
desc 'remove', 'remove route annotation'
|
|
40
|
+
def remove
|
|
41
|
+
raise 'Route file not found' unless @route_file_path.exist?
|
|
26
42
|
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
if remove_annotation(file_path: @route_file_path, marker: 'routes')
|
|
44
|
+
say "remove route annotation in #{@route_file_path}"
|
|
45
|
+
else
|
|
46
|
+
say "no route annotation in #{@route_file_path}"
|
|
47
|
+
end
|
|
29
48
|
end
|
|
30
49
|
|
|
31
|
-
|
|
32
|
-
Pathname.new('config/routes.rb')
|
|
33
|
-
end
|
|
50
|
+
private
|
|
34
51
|
|
|
35
52
|
def parse_routes(routes)
|
|
36
53
|
split_routes = routes.split(/\r\n|\r|\n/)
|
|
@@ -43,13 +60,17 @@ module AwesomeAnnotate
|
|
|
43
60
|
end
|
|
44
61
|
|
|
45
62
|
def insert_file_before_class(file_path, message)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
replace_or_insert_annotation(
|
|
64
|
+
file_path: file_path,
|
|
65
|
+
marker: 'routes',
|
|
66
|
+
content: message,
|
|
67
|
+
before: "Rails.application.routes.draw do\n"
|
|
68
|
+
)
|
|
49
69
|
end
|
|
50
70
|
|
|
51
71
|
def self.source_root
|
|
52
72
|
Dir.pwd
|
|
53
73
|
end
|
|
74
|
+
private_class_method :source_root
|
|
54
75
|
end
|
|
55
76
|
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AwesomeAnnotate
|
|
4
|
+
module SchemaAnnotation
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def schema_annotation(klass)
|
|
8
|
+
columns = klass.columns
|
|
9
|
+
column_name_width = columns.map { |column| column.name.length }.max || 0
|
|
10
|
+
column_type_width = columns.map { |column| column_type(column).length }.max || 0
|
|
11
|
+
|
|
12
|
+
[
|
|
13
|
+
schema_header(klass),
|
|
14
|
+
columns.map { |column| column_annotation(klass, column, column_name_width, column_type_width) }.join,
|
|
15
|
+
index_annotations(klass),
|
|
16
|
+
"#\n"
|
|
17
|
+
].join
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def schema_header(klass)
|
|
21
|
+
[
|
|
22
|
+
"# == Schema Information\n",
|
|
23
|
+
"#\n",
|
|
24
|
+
"# Table name: #{klass.table_name}\n",
|
|
25
|
+
"#\n"
|
|
26
|
+
].join
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def column_annotation(klass, column, column_name_width, column_type_width)
|
|
30
|
+
column_name = column.name.ljust(column_name_width)
|
|
31
|
+
type = column_type(column).ljust(column_type_width)
|
|
32
|
+
details = column_details(klass, column)
|
|
33
|
+
line = "# #{column_name} :#{type}"
|
|
34
|
+
|
|
35
|
+
line = "#{line} #{details.join(', ')}" if details.any?
|
|
36
|
+
"#{line}\n"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def column_type(column)
|
|
40
|
+
column.type.to_s
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def column_details(klass, column)
|
|
44
|
+
details = []
|
|
45
|
+
details << 'not null' if column.null == false
|
|
46
|
+
details << 'primary key' if column.name == klass.primary_key
|
|
47
|
+
details << "default(#{column.default.inspect})" unless column.default.nil?
|
|
48
|
+
details
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def index_annotations(klass)
|
|
52
|
+
indexes = klass.connection.indexes(klass.table_name)
|
|
53
|
+
return '' if indexes.empty?
|
|
54
|
+
|
|
55
|
+
index_column_width = indexes.map { |index| index_columns(index).length }.max || 0
|
|
56
|
+
|
|
57
|
+
[
|
|
58
|
+
"#\n",
|
|
59
|
+
"# Indexes\n",
|
|
60
|
+
"#\n",
|
|
61
|
+
indexes.map { |index| index_annotation(index, index_column_width) }.join
|
|
62
|
+
].join
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def index_annotation(index, index_column_width)
|
|
66
|
+
columns = index_columns(index).ljust(index_column_width)
|
|
67
|
+
details = index_details(index)
|
|
68
|
+
|
|
69
|
+
"# #{columns} #{details.join(', ')}\n"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def index_columns(index)
|
|
73
|
+
"(#{index.columns.join(',')})"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def index_details(index)
|
|
77
|
+
details = []
|
|
78
|
+
details << 'UNIQUE' if index.unique
|
|
79
|
+
details << index.name
|
|
80
|
+
details
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/awesome_annotate.rb
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
|
|
6
|
-
module AwesomeAnnotate
|
|
7
|
-
class Error < StandardError; end
|
|
8
|
-
end
|
|
3
|
+
require_relative 'awesome_annotate/version'
|
|
4
|
+
require_relative 'awesome_annotate/error'
|
|
5
|
+
require_relative 'awesome_annotate/cli'
|
metadata
CHANGED
|
@@ -1,46 +1,52 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: awesome_annotate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- wisdom-plus
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: activerecord
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '6.1'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '8.0'
|
|
20
23
|
type: :runtime
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
26
|
requirements:
|
|
24
27
|
- - ">="
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
29
|
+
version: '6.1'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.0'
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
34
|
+
name: thor
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
|
30
36
|
requirements:
|
|
31
|
-
- - "
|
|
37
|
+
- - "~>"
|
|
32
38
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
39
|
+
version: '1.3'
|
|
34
40
|
type: :runtime
|
|
35
41
|
prerelease: false
|
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
43
|
requirements:
|
|
38
|
-
- - "
|
|
44
|
+
- - "~>"
|
|
39
45
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
46
|
+
version: '1.3'
|
|
41
47
|
description: annotate your code with comments (e.g. model schema, routes, etc.)
|
|
42
48
|
email:
|
|
43
|
-
- wisdom
|
|
49
|
+
- wisdom-plus@users.noreply.github.com
|
|
44
50
|
executables:
|
|
45
51
|
- awesome_annotate
|
|
46
52
|
extensions: []
|
|
@@ -48,16 +54,20 @@ extra_rdoc_files: []
|
|
|
48
54
|
files:
|
|
49
55
|
- ".rspec"
|
|
50
56
|
- ".rubocop.yml"
|
|
57
|
+
- ".ruby-version"
|
|
51
58
|
- CHANGELOG.md
|
|
52
59
|
- LICENSE.txt
|
|
53
60
|
- README.md
|
|
54
61
|
- Rakefile
|
|
55
|
-
- awesome_annotate.gemspec
|
|
56
62
|
- exe/awesome_annotate
|
|
57
63
|
- lib/awesome_annotate.rb
|
|
64
|
+
- lib/awesome_annotate/annotation_block.rb
|
|
58
65
|
- lib/awesome_annotate/cli.rb
|
|
66
|
+
- lib/awesome_annotate/error.rb
|
|
59
67
|
- lib/awesome_annotate/model.rb
|
|
68
|
+
- lib/awesome_annotate/rails_environment.rb
|
|
60
69
|
- lib/awesome_annotate/route.rb
|
|
70
|
+
- lib/awesome_annotate/schema_annotation.rb
|
|
61
71
|
- lib/awesome_annotate/version.rb
|
|
62
72
|
- sig/awesome_annotate.rbs
|
|
63
73
|
homepage: https://github.com/wisdom-plus/awesome_annotate
|
|
@@ -68,6 +78,7 @@ metadata:
|
|
|
68
78
|
homepage_uri: https://github.com/wisdom-plus/awesome_annotate
|
|
69
79
|
source_code_uri: https://github.com/wisdom-plus/awesome_annotate
|
|
70
80
|
changelog_uri: https://github.com/wisdom-plus/awesome_annotate/blob/main/CHANGELOG.md
|
|
81
|
+
rubygems_mfa_required: 'true'
|
|
71
82
|
post_install_message:
|
|
72
83
|
rdoc_options: []
|
|
73
84
|
require_paths:
|
|
@@ -76,14 +87,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
76
87
|
requirements:
|
|
77
88
|
- - ">="
|
|
78
89
|
- !ruby/object:Gem::Version
|
|
79
|
-
version: 3.0
|
|
90
|
+
version: '3.0'
|
|
91
|
+
- - "<"
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '4.0'
|
|
80
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
95
|
requirements:
|
|
82
96
|
- - ">="
|
|
83
97
|
- !ruby/object:Gem::Version
|
|
84
98
|
version: '0'
|
|
85
99
|
requirements: []
|
|
86
|
-
rubygems_version: 3.
|
|
100
|
+
rubygems_version: 3.4.10
|
|
87
101
|
signing_key:
|
|
88
102
|
specification_version: 4
|
|
89
103
|
summary: annotate your code with comments
|
data/awesome_annotate.gemspec
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/awesome_annotate/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = "awesome_annotate"
|
|
7
|
-
spec.version = AwesomeAnnotate::VERSION
|
|
8
|
-
spec.authors = ["wisdom-plus"]
|
|
9
|
-
spec.email = ["wisdom.plus.264.dev@gmail.com"]
|
|
10
|
-
|
|
11
|
-
spec.summary = "annotate your code with comments"
|
|
12
|
-
spec.description = "annotate your code with comments (e.g. model schema, routes, etc.)"
|
|
13
|
-
spec.homepage = "https://github.com/wisdom-plus/awesome_annotate"
|
|
14
|
-
spec.license = "MIT"
|
|
15
|
-
spec.required_ruby_version = ">= 3.0.0"
|
|
16
|
-
|
|
17
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
18
|
-
|
|
19
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
20
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
|
21
|
-
spec.metadata["changelog_uri"] = spec.homepage + "/blob/main/CHANGELOG.md"
|
|
22
|
-
|
|
23
|
-
# Specify which files should be added to the gem when it is released.
|
|
24
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
25
|
-
spec.files = Dir.chdir(__dir__) do
|
|
26
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
27
|
-
(File.expand_path(f) == __FILE__) ||
|
|
28
|
-
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
spec.bindir = "exe"
|
|
32
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
33
|
-
spec.require_paths = ["lib"]
|
|
34
|
-
spec.add_dependency "thor"
|
|
35
|
-
spec.add_dependency "activerecord", ">= 6.1.0"
|
|
36
|
-
end
|