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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 231c9ce46ab9f54c995580b73fc46b2f16df5531ab979b0fa9ce7273e34c5fda
4
- data.tar.gz: 0752a6de349b42c1658a8a907f503f1a16392a8d894af9436877c6cb83dbe68a
3
+ metadata.gz: d120e2f6dba99ee06506353a609f7b68e4ad08213141762b77ca6dacacc854ce
4
+ data.tar.gz: 6bb8098703ec777bfc7dcc9351ca88ed4c2f9655cb122c71f25b398165aad187
5
5
  SHA512:
6
- metadata.gz: 37e53bcca2c79b97e551397f0dd6c92ab0c1f2b833edb05175100b0548c00c8b3117ea4aa528ef9f37e604329805f65f98ba9ac48c6bc2661b6395777de3028e
7
- data.tar.gz: c38e57f6d68825d3917fa0c9926be4ac7526c5a0154c568afc18b802363bd4af1e7e37e22581ea009a0fa5bdb1ab0f0d3c9f1f5f11f12e21dfc7ee3d89a2862a
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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwesomeAnnotate
4
+ class Error < StandardError; end
5
+ class NotFoundError < Error; end
6
+ end
@@ -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
- abort "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
11
50
 
12
- apply env_file_path.to_s
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
- klass = klass_name(model_name)
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, "# Columns: #{column_names.join(', ')}\n")
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
- private
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 env_file_path
29
- Pathname.new('config/environment.rb')
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 model_dir
33
- 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
34
84
  end
35
85
 
36
- def insert_file_before_class(file_path, klass, message)
37
- insert_into_file file_path, :before => /^class\s+\w+\s+<\s+\w+/ do
38
- 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}"
39
103
  end
40
104
  end
41
105
 
42
- def column_names(klass)
43
- 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
+ )
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
- return say "Model file not found"
119
+ say 'Model file not found'
120
+ raise AwesomeAnnotate::NotFoundError
51
121
  end
52
122
 
53
- return file_path
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
- klass = Object.const_get(name)
59
-
128
+ Object.const_get(name)
60
129
  rescue NameError
61
- say "Model not found"
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
@@ -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,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
- abort "Rails application path is required" unless env_file_path.exist?
22
+ raise 'Rails application path is required' unless @env_file_path.exist?
11
23
 
12
- apply env_file_path.to_s
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
- insert_file_before_class(route_file_path, route_message)
32
+ raise 'Route file not found' unless @route_file_path.exist?
21
33
 
22
- say "annotate routes in #{route_file_path}"
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
- private
39
+ desc 'remove', 'remove route annotation'
40
+ def remove
41
+ raise 'Route file not found' unless @route_file_path.exist?
26
42
 
27
- def env_file_path
28
- Pathname.new('config/environment.rb')
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
- def route_file_path
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
- 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.4"
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,46 +1,52 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: awesome_annotate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
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-06-07 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: []
@@ -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.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.5.3
100
+ rubygems_version: 3.4.10
87
101
  signing_key:
88
102
  specification_version: 4
89
103
  summary: annotate your code with comments
@@ -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