awesome_annotate 0.1.5 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: add6a26de84b0dab25e21c0c60213a8d0c6aef0109a552faf635fdfb745e5bcb
4
- data.tar.gz: 6a9494f2aeafe2e163c59f1544f181da0d39aef6736851412580c1b59231631a
3
+ metadata.gz: d120e2f6dba99ee06506353a609f7b68e4ad08213141762b77ca6dacacc854ce
4
+ data.tar.gz: 6bb8098703ec777bfc7dcc9351ca88ed4c2f9655cb122c71f25b398165aad187
5
5
  SHA512:
6
- metadata.gz: 9654317d619c0d5e4706b1a5fb1f6899a123a3f8b6acb79c29d744d65ed5c6d1a895e9cbd835653e1d33de80055c75a24e0f38fd365d2e58112738cf2e70b763
7
- data.tar.gz: 807524f834b4087643d8fc2e2477ebd271f6eac9a989d947b9ac3dd693259baf7890187e3201293d4e809f6dfef9f7abe05542f3a287e17af0a527d54d955acc
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
- TODO: Delete this and the text below, and describe your gem
3
+ AwesomeAnnotate adds generated schema and route comments to Rails applications.
4
4
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/awesome_annotate`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
23
+ Add this line to your Rails application's Gemfile:
24
+
25
+ ```ruby
26
+ gem 'awesome_annotate'
27
+ ```
10
28
 
11
- Install the gem and add to the application's Gemfile by executing:
29
+ Then run:
12
30
 
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
31
+ ```sh
32
+ bundle install
33
+ ```
14
34
 
15
- If bundler is not being used to manage dependencies, install the gem by executing:
35
+ If you are using this repository directly before publishing the gem, use a Git
36
+ source instead:
16
37
 
17
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
38
+ ```ruby
39
+ gem 'awesome_annotate', git: 'https://github.com/wisdom-plus/awesome_annotate'
40
+ ```
18
41
 
19
42
  ## Usage
20
43
 
21
- TODO: Write usage instructions here
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
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
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 https://github.com/[USERNAME]/awesome_annotate.
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 [MIT License](https://opensource.org/licenses/MIT).
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 "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- require "rubocop/rake_task"
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
@@ -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 "--version, -v", "print the version"
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', "Writes application route information to `config/routes.rb`."
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
- private
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,2 +1,6 @@
1
+ # frozen_string_literal: true
1
2
 
2
- class NotFoundError < StandardError; end
3
+ module AwesomeAnnotate
4
+ class Error < StandardError; end
5
+ class NotFoundError < Error; end
6
+ end
@@ -1,10 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record'
2
4
  require 'thor'
5
+ require_relative 'annotation_block'
3
6
  require_relative 'error'
7
+ require_relative 'rails_environment'
8
+ require_relative 'schema_annotation'
4
9
 
5
10
  module AwesomeAnnotate
6
11
  class Model < Thor
12
+ include AnnotationBlock
7
13
  include Thor::Actions
14
+ include RailsEnvironment
15
+ include SchemaAnnotation
8
16
 
9
17
  def initialize(params = {})
10
18
  super()
@@ -14,60 +22,118 @@ module AwesomeAnnotate
14
22
 
15
23
  desc 'model [model name]', 'annotate your model'
16
24
  def annotate(model_name)
17
- raise "Rails application path is required" unless @env_file_path.exist?
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
50
+
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
18
59
 
19
- apply @env_file_path.to_s
60
+ private
20
61
 
21
- klass = klass_name(model_name)
62
+ def annotate_loaded_model(model_name, report_missing: true)
63
+ klass = klass_name(model_name, report_missing: report_missing)
22
64
 
23
65
  return say 'This model does not inherit activerecord' unless klass < ActiveRecord::Base
24
66
 
25
- column_names = column_names(klass)
26
67
  file_path = model_file_path(model_name)
27
68
 
28
- insert_file_before_class(file_path, klass, "# Columns: #{column_names.join(', ')}\n")
69
+ insert_file_before_class(file_path, schema_annotation(klass))
29
70
 
30
71
  say "annotate #{model_name.pluralize} table columns in #{file_path}"
31
72
  end
32
73
 
33
- private
74
+ def discover_model_names
75
+ discovered_model_file_paths.map { |file_path| model_name_from_file_path(file_path) }
76
+ end
77
+
78
+ def discovered_model_file_paths
79
+ Dir.glob(@model_dir.join('**/*.rb')).reject { |file_path| excluded_model_file?(file_path) }
80
+ end
34
81
 
35
- def model_dir
36
- Pathname.new('app/models')
82
+ def model_name_from_file_path(file_path)
83
+ Pathname.new(file_path).relative_path_from(@model_dir).sub_ext('').to_s
37
84
  end
38
85
 
39
- def insert_file_before_class(file_path, klass, message)
40
- insert_into_file file_path, :before => /^class\s+\w+\s+<\s+\w+/ do
41
- message
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}"
42
103
  end
43
104
  end
44
105
 
45
- def column_names(klass)
46
- klass.column_names
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
+ )
47
113
  end
48
114
 
49
115
  def model_file_path(model_name)
50
116
  file_path = "#{@model_dir}/#{model_name}.rb"
51
117
 
52
118
  unless File.exist?(file_path)
53
- say "Model file not found"
54
- raise NotFoundError
119
+ say 'Model file not found'
120
+ raise AwesomeAnnotate::NotFoundError
55
121
  end
56
122
 
57
- return file_path
123
+ file_path
58
124
  end
59
125
 
60
- def klass_name(model_name)
126
+ def klass_name(model_name, report_missing: true)
61
127
  name = model_name.singularize.camelize
62
- return Object.const_get(name)
63
-
128
+ Object.const_get(name)
64
129
  rescue NameError
65
- say "Model not found"
66
- raise NotFoundError
130
+ say 'Model not found' if report_missing
131
+ raise AwesomeAnnotate::NotFoundError
67
132
  end
68
133
 
69
134
  def self.source_root
70
135
  Dir.pwd
71
136
  end
137
+ private_class_method :source_root
72
138
  end
73
139
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwesomeAnnotate
4
+ module RailsEnvironment
5
+ private
6
+
7
+ def load_rails_environment
8
+ require @env_file_path.expand_path.to_s
9
+ end
10
+ end
11
+ end
@@ -1,9 +1,15 @@
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
7
13
 
8
14
  def initialize(params = {})
9
15
  super()
@@ -13,9 +19,9 @@ module AwesomeAnnotate
13
19
 
14
20
  desc 'annotate all routes', 'annotate your routes'
15
21
  def annotate
16
- raise "Rails application path is required" unless @env_file_path.exist?
22
+ raise 'Rails application path is required' unless @env_file_path.exist?
17
23
 
18
- apply @env_file_path.to_s
24
+ load_rails_environment
19
25
 
20
26
  inspector = ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes)
21
27
  formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet.new
@@ -23,13 +29,24 @@ module AwesomeAnnotate
23
29
  routes = inspector.format(formatter, {})
24
30
  route_message = parse_routes(routes)
25
31
 
26
- raise "Route file not found" unless @route_file_path.exist?
32
+ raise 'Route file not found' unless @route_file_path.exist?
27
33
 
28
34
  insert_file_before_class(@route_file_path, route_message)
29
35
 
30
36
  say "annotate routes in #{@route_file_path}"
31
37
  end
32
38
 
39
+ desc 'remove', 'remove route annotation'
40
+ def remove
41
+ raise 'Route file not found' unless @route_file_path.exist?
42
+
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
48
+ end
49
+
33
50
  private
34
51
 
35
52
  def parse_routes(routes)
@@ -43,13 +60,17 @@ module AwesomeAnnotate
43
60
  end
44
61
 
45
62
  def insert_file_before_class(file_path, message)
46
- insert_into_file file_path, :before => "Rails.application.routes.draw do\n" do
47
- message
48
- end
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AwesomeAnnotate
4
- VERSION = "0.1.5"
4
+ VERSION = '0.2.0'
5
5
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "awesome_annotate/version"
4
- require_relative "awesome_annotate/cli"
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,65 +1,73 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: awesome_annotate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
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: 2024-09-03 00:00:00.000000000 Z
11
+ date: 2026-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: thor
14
+ name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
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: '0'
29
+ version: '6.1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '8.0'
27
33
  - !ruby/object:Gem::Dependency
28
- name: activerecord
34
+ name: thor
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - ">="
37
+ - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: 6.1.0
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: 6.1.0
46
+ version: '1.3'
41
47
  description: annotate your code with comments (e.g. model schema, routes, etc.)
42
48
  email:
43
- - wisdom.plus.264.dev@gmail.com
49
+ - wisdom-plus@users.noreply.github.com
44
50
  executables:
45
51
  - awesome_annotate
46
52
  extensions: []
47
53
  extra_rdoc_files: []
48
54
  files:
49
55
  - ".rspec"
50
- - ".rspec_status"
51
56
  - ".rubocop.yml"
57
+ - ".ruby-version"
52
58
  - CHANGELOG.md
53
59
  - LICENSE.txt
54
60
  - README.md
55
61
  - Rakefile
56
- - awesome_annotate.gemspec
57
62
  - exe/awesome_annotate
58
63
  - lib/awesome_annotate.rb
64
+ - lib/awesome_annotate/annotation_block.rb
59
65
  - lib/awesome_annotate/cli.rb
60
66
  - lib/awesome_annotate/error.rb
61
67
  - lib/awesome_annotate/model.rb
68
+ - lib/awesome_annotate/rails_environment.rb
62
69
  - lib/awesome_annotate/route.rb
70
+ - lib/awesome_annotate/schema_annotation.rb
63
71
  - lib/awesome_annotate/version.rb
64
72
  - sig/awesome_annotate.rbs
65
73
  homepage: https://github.com/wisdom-plus/awesome_annotate
@@ -70,6 +78,7 @@ metadata:
70
78
  homepage_uri: https://github.com/wisdom-plus/awesome_annotate
71
79
  source_code_uri: https://github.com/wisdom-plus/awesome_annotate
72
80
  changelog_uri: https://github.com/wisdom-plus/awesome_annotate/blob/main/CHANGELOG.md
81
+ rubygems_mfa_required: 'true'
73
82
  post_install_message:
74
83
  rdoc_options: []
75
84
  require_paths:
@@ -78,14 +87,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
78
87
  requirements:
79
88
  - - ">="
80
89
  - !ruby/object:Gem::Version
81
- version: 3.0.0
90
+ version: '3.0'
91
+ - - "<"
92
+ - !ruby/object:Gem::Version
93
+ version: '4.0'
82
94
  required_rubygems_version: !ruby/object:Gem::Requirement
83
95
  requirements:
84
96
  - - ">="
85
97
  - !ruby/object:Gem::Version
86
98
  version: '0'
87
99
  requirements: []
88
- rubygems_version: 3.5.3
100
+ rubygems_version: 3.4.10
89
101
  signing_key:
90
102
  specification_version: 4
91
103
  summary: annotate your code with comments
data/.rspec_status DELETED
@@ -1,11 +0,0 @@
1
- example_id | status | run_time |
2
- ---------------------------------------------------- | ------ | --------------- |
3
- ./spec/awesome_annotate_spec.rb[1:1] | passed | 0.00031 seconds |
4
- ./spec/lib/awesome_annotate/cli_spec.rb[1:3:1] | passed | 0.00185 seconds |
5
- ./spec/lib/awesome_annotate/model_spec.rb[1:1:1:1:1] | passed | 0.19755 seconds |
6
- ./spec/lib/awesome_annotate/model_spec.rb[1:1:1:2:1] | passed | 0.00078 seconds |
7
- ./spec/lib/awesome_annotate/model_spec.rb[1:1:1:3:1] | passed | 0.00018 seconds |
8
- ./spec/lib/awesome_annotate/model_spec.rb[1:1:2:1] | passed | 0.00008 seconds |
9
- ./spec/lib/awesome_annotate/route_spec.rb[1:1:1:1:1] | passed | 0.00303 seconds |
10
- ./spec/lib/awesome_annotate/route_spec.rb[1:1:1:2:1] | passed | 0.0005 seconds |
11
- ./spec/lib/awesome_annotate/route_spec.rb[1:1:2:1] | passed | 0.00009 seconds |
@@ -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