annotaterb 4.0.0.beta.1
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 +7 -0
- data/CHANGELOG.md +0 -0
- data/LICENSE.txt +55 -0
- data/README.md +91 -0
- data/VERSION +1 -0
- data/exe/annotaterb +21 -0
- data/lib/annotate_rb/active_record_patch.rb +9 -0
- data/lib/annotate_rb/commands/annotate_models.rb +22 -0
- data/lib/annotate_rb/commands/annotate_routes.rb +19 -0
- data/lib/annotate_rb/commands/print_help.rb +16 -0
- data/lib/annotate_rb/commands/print_version.rb +12 -0
- data/lib/annotate_rb/commands.rb +10 -0
- data/lib/annotate_rb/config_finder.rb +21 -0
- data/lib/annotate_rb/config_loader.rb +63 -0
- data/lib/annotate_rb/core.rb +23 -0
- data/lib/annotate_rb/eager_loader.rb +23 -0
- data/lib/annotate_rb/env.rb +30 -0
- data/lib/annotate_rb/model_annotator/annotation_pattern_generator.rb +19 -0
- data/lib/annotate_rb/model_annotator/annotator.rb +74 -0
- data/lib/annotate_rb/model_annotator/bad_model_file_error.rb +11 -0
- data/lib/annotate_rb/model_annotator/constants.rb +22 -0
- data/lib/annotate_rb/model_annotator/file_annotation_remover.rb +25 -0
- data/lib/annotate_rb/model_annotator/file_annotator.rb +79 -0
- data/lib/annotate_rb/model_annotator/file_name_resolver.rb +16 -0
- data/lib/annotate_rb/model_annotator/file_patterns.rb +129 -0
- data/lib/annotate_rb/model_annotator/helper.rb +54 -0
- data/lib/annotate_rb/model_annotator/model_class_getter.rb +63 -0
- data/lib/annotate_rb/model_annotator/model_file_annotator.rb +118 -0
- data/lib/annotate_rb/model_annotator/model_files_getter.rb +62 -0
- data/lib/annotate_rb/model_annotator/pattern_getter.rb +27 -0
- data/lib/annotate_rb/model_annotator/schema_info.rb +480 -0
- data/lib/annotate_rb/model_annotator.rb +20 -0
- data/lib/annotate_rb/options.rb +204 -0
- data/lib/annotate_rb/parser.rb +385 -0
- data/lib/annotate_rb/rake_bootstrapper.rb +34 -0
- data/lib/annotate_rb/route_annotator/annotation_processor.rb +56 -0
- data/lib/annotate_rb/route_annotator/annotator.rb +40 -0
- data/lib/annotate_rb/route_annotator/base_processor.rb +104 -0
- data/lib/annotate_rb/route_annotator/header_generator.rb +113 -0
- data/lib/annotate_rb/route_annotator/helper.rb +104 -0
- data/lib/annotate_rb/route_annotator/removal_processor.rb +40 -0
- data/lib/annotate_rb/route_annotator.rb +12 -0
- data/lib/annotate_rb/runner.rb +34 -0
- data/lib/annotate_rb/tasks/annotate_models_migrate.rake +30 -0
- data/lib/annotate_rb.rb +30 -0
- data/lib/generators/annotate_rb/USAGE +4 -0
- data/lib/generators/annotate_rb/install_generator.rb +15 -0
- data/lib/generators/annotate_rb/templates/auto_annotate_models.rake +7 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4ca3a25c118bfa3f5910e4fe455de3d9333c353d409f434cffde12694f87cff5
|
4
|
+
data.tar.gz: 417dcf943056ec0f44ba7eb973c3ad2dc99cb435315d41a13d1fc9f53bb87b9e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2d34ca80e5686ef6cadfe0a2624aecd891fde3628653e68fa8b86613ac6382a4d7bdcf8e0c7504988dd653023be3d1190c5a072d46890b9c874ecaf1abbc20f7
|
7
|
+
data.tar.gz: 2ae8ca4a18c83437b379f7823291c094a87db9465742bf2c1c94c99fcfe1cb833f894367774a44ff2a9e7c8f41f9670f8baeedff1d5832bbbf2c49a6ced8ac80
|
data/CHANGELOG.md
ADDED
File without changes
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
You can redistribute it and/or modify it under either the terms of the
|
2
|
+
2-clause BSDL (see the file BSDL), or the conditions below:
|
3
|
+
|
4
|
+
1. You may make and give away verbatim copies of the source form of the
|
5
|
+
software without restriction, provided that you duplicate all of the
|
6
|
+
original copyright notices and associated disclaimers.
|
7
|
+
|
8
|
+
2. You may modify your copy of the software in any way, provided that
|
9
|
+
you do at least ONE of the following:
|
10
|
+
|
11
|
+
a) place your modifications in the Public Domain or otherwise
|
12
|
+
make them Freely Available, such as by posting said
|
13
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
14
|
+
the author to include your modifications in the software.
|
15
|
+
|
16
|
+
b) use the modified software only within your corporation or
|
17
|
+
organization.
|
18
|
+
|
19
|
+
c) give non-standard binaries non-standard names, with
|
20
|
+
instructions on where to get the original software distribution.
|
21
|
+
|
22
|
+
d) make other distribution arrangements with the author.
|
23
|
+
|
24
|
+
3. You may distribute the software in object code or binary form,
|
25
|
+
provided that you do at least ONE of the following:
|
26
|
+
|
27
|
+
a) distribute the binaries and library files of the software,
|
28
|
+
together with instructions (in the manual page or equivalent)
|
29
|
+
on where to get the original distribution.
|
30
|
+
|
31
|
+
b) accompany the distribution with the machine-readable source of
|
32
|
+
the software.
|
33
|
+
|
34
|
+
c) give non-standard binaries non-standard names, with
|
35
|
+
instructions on where to get the original software distribution.
|
36
|
+
|
37
|
+
d) make other distribution arrangements with the author.
|
38
|
+
|
39
|
+
4. You may modify and include the part of the software into any other
|
40
|
+
software (possibly commercial). But some files in the distribution
|
41
|
+
are not written by the author, so that they are not under these terms.
|
42
|
+
|
43
|
+
For the list of those files and their copying conditions, see the
|
44
|
+
file LEGAL.
|
45
|
+
|
46
|
+
5. The scripts and library files supplied as input to or produced as
|
47
|
+
output from the software do not automatically fall under the
|
48
|
+
copyright of the software, but belong to whomever generated them,
|
49
|
+
and may be sold commercially, and may be aggregated with this
|
50
|
+
software.
|
51
|
+
|
52
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
53
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
54
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
55
|
+
PURPOSE.
|
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
## AnnotateRb
|
2
|
+
### forked from the [Annotate aka AnnotateModels gem](https://github.com/ctran/annotate_models)
|
3
|
+
|
4
|
+
----------
|
5
|
+
[](https://github.com/drwl/annotaterb/actions/workflows/ci.yml)
|
6
|
+
|
7
|
+
Adds comments summarizing the model schema or routes in your:
|
8
|
+
|
9
|
+
- ActiveRecord models
|
10
|
+
- Fixture files
|
11
|
+
- Tests and Specs
|
12
|
+
- FactoryBot factories
|
13
|
+
- `routes.rb` file (for Rails projects)
|
14
|
+
|
15
|
+
The schema comment looks like this:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
# == Schema Information
|
19
|
+
#
|
20
|
+
# Table name: tasks
|
21
|
+
#
|
22
|
+
# id :integer not null, primary key
|
23
|
+
# content :string
|
24
|
+
# count :integer
|
25
|
+
# status :boolean
|
26
|
+
# created_at :datetime not null
|
27
|
+
# updated_at :datetime not null
|
28
|
+
#
|
29
|
+
class Task < ApplicationRecord
|
30
|
+
...
|
31
|
+
```
|
32
|
+
----------
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
```sh
|
36
|
+
$ gem install annotaterb
|
37
|
+
```
|
38
|
+
|
39
|
+
Or install it into your Rails project through the Gemfile:
|
40
|
+
|
41
|
+
```rb
|
42
|
+
group :development do
|
43
|
+
...
|
44
|
+
|
45
|
+
gem "annotaterb"
|
46
|
+
|
47
|
+
...
|
48
|
+
```
|
49
|
+
|
50
|
+
### Automatically annotate models
|
51
|
+
For Rails projects, model files can get automatically annotated after migration tasks. To do this, run the following command:
|
52
|
+
|
53
|
+
```sh
|
54
|
+
$ bin/rails g annotate_rb:install
|
55
|
+
```
|
56
|
+
|
57
|
+
This will copy a rake task into your Rails project's `lib/tasks` directory that will hook into the Rails project rake tasks, automatically running AnnotateRb after database migration rake tasks.
|
58
|
+
|
59
|
+
## Migrating from the annotate gem
|
60
|
+
|
61
|
+
Add steps for migrating from annotate gem.
|
62
|
+
|
63
|
+
## Usage
|
64
|
+
|
65
|
+
AnnotateRb has a CLI that you can use to add or remove annotations.
|
66
|
+
|
67
|
+
```sh
|
68
|
+
# To show the CLI options
|
69
|
+
$ bundle exec annotaterb
|
70
|
+
```
|
71
|
+
|
72
|
+
## Configuration
|
73
|
+
|
74
|
+
|
75
|
+
### How to skip annotating a particular model
|
76
|
+
If you want to always skip annotations on a particular model, add this string
|
77
|
+
anywhere in the file:
|
78
|
+
|
79
|
+
# -*- SkipSchemaAnnotations
|
80
|
+
|
81
|
+
## Sorting
|
82
|
+
|
83
|
+
By default, columns will be sorted in database order (i.e. the order in which
|
84
|
+
migrations were run).
|
85
|
+
|
86
|
+
If you prefer to sort alphabetically so that the results of annotation are
|
87
|
+
consistent regardless of what order migrations are executed in, use `--sort`.
|
88
|
+
|
89
|
+
## License
|
90
|
+
|
91
|
+
Released under the same license as Ruby. No Support. No Warranty.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
4.0.0.beta.1
|
data/exe/annotaterb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
unless File.exist?('./Rakefile') || File.exist?('./Gemfile')
|
5
|
+
abort 'Please run annotaterb from the root of the project.'
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'bundler'
|
10
|
+
Bundler.setup
|
11
|
+
rescue StandardError
|
12
|
+
end
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift("#{__dir__}/../lib")
|
15
|
+
|
16
|
+
require 'annotate_rb'
|
17
|
+
|
18
|
+
exit_status = ::AnnotateRb::Runner.run(ARGV)
|
19
|
+
|
20
|
+
# TODO: Return exit status
|
21
|
+
# exit exit_status
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module Commands
|
5
|
+
class AnnotateModels
|
6
|
+
def call(options)
|
7
|
+
puts "Annotating models"
|
8
|
+
|
9
|
+
if options[:debug]
|
10
|
+
puts "Running with debug mode, options:"
|
11
|
+
pp options.to_h
|
12
|
+
end
|
13
|
+
|
14
|
+
# Eager load Models when we're annotating models
|
15
|
+
AnnotateRb::EagerLoader.call(options)
|
16
|
+
|
17
|
+
AnnotateRb::ModelAnnotator::Annotator.send(options[:target_action], options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module Commands
|
5
|
+
class AnnotateRoutes
|
6
|
+
def call(options)
|
7
|
+
puts "Annotating routes"
|
8
|
+
|
9
|
+
if options[:debug]
|
10
|
+
puts "Running with debug mode, options:"
|
11
|
+
pp options.to_h
|
12
|
+
end
|
13
|
+
|
14
|
+
AnnotateRb::RouteAnnotator::Annotator.send(options[:target_action], options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module Commands
|
5
|
+
autoload :PrintVersion, 'annotate_rb/commands/print_version'
|
6
|
+
autoload :PrintHelp, 'annotate_rb/commands/print_help'
|
7
|
+
autoload :AnnotateModels, 'annotate_rb/commands/annotate_models'
|
8
|
+
autoload :AnnotateRoutes, 'annotate_rb/commands/annotate_routes'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
class ConfigFinder
|
5
|
+
DOTFILE = '.annotaterb.yml'
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def find_project_root
|
9
|
+
# We should expect this method to be called from a Rails project root and returning it
|
10
|
+
# e.g. "/Users/drwl/personal/annotaterb/dummyapp"
|
11
|
+
Dir.pwd
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_project_dotfile
|
15
|
+
file_path = File.expand_path(DOTFILE, find_project_root)
|
16
|
+
|
17
|
+
return file_path if File.exist?(file_path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
# Raised when a configuration file is not found.
|
5
|
+
class ConfigNotFoundError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class ConfigLoader
|
9
|
+
class << self
|
10
|
+
def load_config
|
11
|
+
config_path = ConfigFinder.find_project_dotfile
|
12
|
+
|
13
|
+
if config_path
|
14
|
+
load_yaml_configuration(config_path)
|
15
|
+
else
|
16
|
+
{}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Method from Rubocop::ConfigLoader
|
21
|
+
def load_yaml_configuration(absolute_path)
|
22
|
+
file_contents = read_file(absolute_path)
|
23
|
+
|
24
|
+
hash = yaml_safe_load(file_contents, absolute_path) || {}
|
25
|
+
|
26
|
+
# TODO: Print config if debug flag/option is set
|
27
|
+
|
28
|
+
raise(TypeError, "Malformed configuration in #{absolute_path}") unless hash.is_a?(Hash)
|
29
|
+
|
30
|
+
hash
|
31
|
+
end
|
32
|
+
|
33
|
+
# Read the specified file, or exit with a friendly, concise message on
|
34
|
+
# stderr. Care is taken to use the standard OS exit code for a "file not
|
35
|
+
# found" error.
|
36
|
+
#
|
37
|
+
# Method from Rubocop::ConfigLoader
|
38
|
+
def read_file(absolute_path)
|
39
|
+
File.read(absolute_path, encoding: Encoding::UTF_8)
|
40
|
+
rescue Errno::ENOENT
|
41
|
+
raise ConfigNotFoundError, "Configuration file not found: #{absolute_path}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Method from Rubocop::ConfigLoader
|
45
|
+
def yaml_safe_load(yaml_code, filename)
|
46
|
+
yaml_safe_load!(yaml_code, filename)
|
47
|
+
rescue ::StandardError
|
48
|
+
if defined?(::SafeYAML)
|
49
|
+
raise 'SafeYAML is unmaintained, no longer needed and should be removed'
|
50
|
+
end
|
51
|
+
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
|
55
|
+
# Method from Rubocop::ConfigLoader
|
56
|
+
def yaml_safe_load!(yaml_code, filename)
|
57
|
+
YAML.safe_load(
|
58
|
+
yaml_code, permitted_classes: [Regexp, Symbol], aliases: true, filename: filename, symbolize_names: true
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module Core
|
5
|
+
class << self
|
6
|
+
def version
|
7
|
+
@version ||= File.read(File.expand_path('../../VERSION', __dir__)).strip
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_rake_tasks
|
11
|
+
return if @load_rake_tasks
|
12
|
+
|
13
|
+
rake_tasks = Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')]
|
14
|
+
|
15
|
+
rake_tasks.each do |task|
|
16
|
+
load task
|
17
|
+
end
|
18
|
+
|
19
|
+
@load_rake_tasks = true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
# Not sure what this does just yet
|
5
|
+
class EagerLoader
|
6
|
+
class << self
|
7
|
+
def call(options)
|
8
|
+
options[:require].count > 0 && options[:require].each { |path| require path }
|
9
|
+
|
10
|
+
if defined?(::Rails::Application)
|
11
|
+
klass = ::Rails::Application.send(:subclasses).first
|
12
|
+
klass.eager_load!
|
13
|
+
else
|
14
|
+
options[:model_dir].each do |dir|
|
15
|
+
::Rake::FileList["#{dir}/**/*.rb"].each do |fname|
|
16
|
+
require File.expand_path(fname)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
class Env
|
5
|
+
class << self
|
6
|
+
def read(key)
|
7
|
+
key = key.to_s unless key.is_a?(String)
|
8
|
+
|
9
|
+
ENV[key]
|
10
|
+
end
|
11
|
+
|
12
|
+
def write(key, value)
|
13
|
+
key = key.to_s unless key.is_a?(String)
|
14
|
+
|
15
|
+
ENV[key] = value.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch(key, default_value)
|
19
|
+
key = key.to_s unless key.is_a?(String)
|
20
|
+
val = read(key)
|
21
|
+
|
22
|
+
if val.nil?
|
23
|
+
default_value
|
24
|
+
else
|
25
|
+
val
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module ModelAnnotator
|
5
|
+
class AnnotationPatternGenerator
|
6
|
+
COMPAT_PREFIX = '== Schema Info'.freeze
|
7
|
+
COMPAT_PREFIX_MD = '## Schema Info'.freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def call(options = Options.from({}))
|
11
|
+
if options[:wrapper_open]
|
12
|
+
return /(?:^(\n|\r\n)?# (?:#{options[:wrapper_open]}).*(\n|\r\n)?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?(\n|\r\n)(#.*(\n|\r\n))*(\n|\r\n)*)|^(\n|\r\n)?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?(\n|\r\n)(#.*(\n|\r\n))*(\n|\r\n)*/
|
13
|
+
end
|
14
|
+
/^(\n|\r\n)?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?(\n|\r\n)(#.*(\n|\r\n))*(\n|\r\n)*/
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# require 'bigdecimal'
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module ModelAnnotator
|
5
|
+
class Annotator
|
6
|
+
# Annotate Models plugin use this header
|
7
|
+
PREFIX = '== Schema Information'.freeze
|
8
|
+
PREFIX_MD = '## Schema Information'.freeze
|
9
|
+
|
10
|
+
MAGIC_COMMENT_MATCHER = Regexp.new(/(^#\s*encoding:.*(?:\n|r\n))|(^# coding:.*(?:\n|\r\n))|(^# -\*- coding:.*(?:\n|\r\n))|(^# -\*- encoding\s?:.*(?:\n|\r\n))|(^#\s*frozen_string_literal:.+(?:\n|\r\n))|(^# -\*- frozen_string_literal\s*:.+-\*-(?:\n|\r\n))/).freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# We're passed a name of things that might be
|
14
|
+
# ActiveRecord models. If we can find the class, and
|
15
|
+
# if its a subclass of ActiveRecord::Base,
|
16
|
+
# then pass it to the associated block
|
17
|
+
def do_annotations(options = {})
|
18
|
+
header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup
|
19
|
+
version = ActiveRecord::Migrator.current_version rescue 0
|
20
|
+
if options[:include_version] && version > 0
|
21
|
+
header << "\n# Schema version: #{version}"
|
22
|
+
end
|
23
|
+
|
24
|
+
annotated = []
|
25
|
+
model_files_to_annotate = ModelFilesGetter.call(options)
|
26
|
+
|
27
|
+
model_files_to_annotate.each do |path, filename|
|
28
|
+
ModelFileAnnotator.call(annotated, File.join(path, filename), header, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
if annotated.empty?
|
32
|
+
puts 'Model files unchanged.'
|
33
|
+
else
|
34
|
+
puts "Annotated (#{annotated.length}): #{annotated.join(', ')}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_annotations(options = {})
|
39
|
+
deannotated = []
|
40
|
+
deannotated_klass = false
|
41
|
+
ModelFilesGetter.call(options).each do |file|
|
42
|
+
file = File.join(file)
|
43
|
+
begin
|
44
|
+
klass = ModelClassGetter.call(file, options)
|
45
|
+
if klass < ActiveRecord::Base && !klass.abstract_class?
|
46
|
+
model_name = klass.name.underscore
|
47
|
+
table_name = klass.table_name
|
48
|
+
model_file_name = file
|
49
|
+
deannotated_klass = true if FileAnnotationRemover.call(model_file_name, options)
|
50
|
+
|
51
|
+
patterns = PatternGetter.call(options)
|
52
|
+
|
53
|
+
patterns
|
54
|
+
.map { |f| FileNameResolver.call(f, model_name, table_name) }
|
55
|
+
.each do |f|
|
56
|
+
if File.exist?(f)
|
57
|
+
FileAnnotationRemover.call(f, options)
|
58
|
+
deannotated_klass = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
deannotated << klass if deannotated_klass
|
63
|
+
rescue StandardError => e
|
64
|
+
$stderr.puts "Unable to deannotate #{File.join(file)}: #{e.message}"
|
65
|
+
$stderr.puts "\t" + e.backtrace.join("\n\t") if options[:trace]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
puts "Removed annotations from: #{deannotated.join(', ')}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AnnotateRb
|
2
|
+
module ModelAnnotator
|
3
|
+
module Constants
|
4
|
+
TRUE_RE = /^(true|t|yes|y|1)$/i.freeze
|
5
|
+
|
6
|
+
##
|
7
|
+
# The set of available options to customize the behavior of Annotate.
|
8
|
+
#
|
9
|
+
POSITION_OPTIONS = ::AnnotateRb::Options::POSITION_OPTION_KEYS
|
10
|
+
|
11
|
+
FLAG_OPTIONS = ::AnnotateRb::Options::FLAG_OPTION_KEYS
|
12
|
+
|
13
|
+
OTHER_OPTIONS = ::AnnotateRb::Options::OTHER_OPTION_KEYS
|
14
|
+
|
15
|
+
PATH_OPTIONS = ::AnnotateRb::Options::PATH_OPTION_KEYS
|
16
|
+
|
17
|
+
ALL_ANNOTATE_OPTIONS = ::AnnotateRb::Options::ALL_OPTION_KEYS
|
18
|
+
|
19
|
+
SKIP_ANNOTATION_PREFIX = '# -\*- SkipSchemaAnnotations'.freeze
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module ModelAnnotator
|
5
|
+
class FileAnnotationRemover
|
6
|
+
class << self
|
7
|
+
def call(file_name, options = Options.from({}))
|
8
|
+
if File.exist?(file_name)
|
9
|
+
content = File.read(file_name)
|
10
|
+
return false if content =~ /#{Constants::SKIP_ANNOTATION_PREFIX}.*\n/
|
11
|
+
|
12
|
+
wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : ''
|
13
|
+
content.sub!(/(#{wrapper_open})?#{AnnotationPatternGenerator.call(options)}/, '')
|
14
|
+
|
15
|
+
File.open(file_name, 'wb') { |f| f.puts content }
|
16
|
+
|
17
|
+
true
|
18
|
+
else
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnnotateRb
|
4
|
+
module ModelAnnotator
|
5
|
+
class FileAnnotator
|
6
|
+
class << self
|
7
|
+
# Add a schema block to a file. If the file already contains
|
8
|
+
# a schema info block (a comment starting with "== Schema Information"),
|
9
|
+
# check if it matches the block that is already there. If so, leave it be.
|
10
|
+
# If not, remove the old info block and write a new one.
|
11
|
+
#
|
12
|
+
# == Returns:
|
13
|
+
# true or false depending on whether the file was modified.
|
14
|
+
#
|
15
|
+
# === Options (opts)
|
16
|
+
# :force<Symbol>:: whether to update the file even if it doesn't seem to need it.
|
17
|
+
# :position_in_*<Symbol>:: where to place the annotated section in fixture or model file,
|
18
|
+
# :before, :top, :after or :bottom. Default is :before.
|
19
|
+
#
|
20
|
+
def call(file_name, info_block, position, options = {})
|
21
|
+
return false unless File.exist?(file_name)
|
22
|
+
old_content = File.read(file_name)
|
23
|
+
return false if old_content =~ /#{Constants::SKIP_ANNOTATION_PREFIX}.*\n/
|
24
|
+
|
25
|
+
# Ignore the Schema version line because it changes with each migration
|
26
|
+
header_pattern = /(^# Table name:.*?\n(#.*[\r]?\n)*[\r]?)/
|
27
|
+
old_header = old_content.match(header_pattern).to_s
|
28
|
+
new_header = info_block.match(header_pattern).to_s
|
29
|
+
|
30
|
+
column_pattern = /^#[\t ]+[\w\*\.`]+[\t ]+.+$/
|
31
|
+
old_columns = old_header && old_header.scan(column_pattern).sort
|
32
|
+
new_columns = new_header && new_header.scan(column_pattern).sort
|
33
|
+
|
34
|
+
return false if old_columns == new_columns && !options[:force]
|
35
|
+
|
36
|
+
abort "annotate error. #{file_name} needs to be updated, but annotate was run with `--frozen`." if options[:frozen]
|
37
|
+
|
38
|
+
# Replace inline the old schema info with the new schema info
|
39
|
+
wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : ""
|
40
|
+
wrapper_close = options[:wrapper_close] ? "# #{options[:wrapper_close]}\n" : ""
|
41
|
+
wrapped_info_block = "#{wrapper_open}#{info_block}#{wrapper_close}"
|
42
|
+
|
43
|
+
annotation_pattern = AnnotationPatternGenerator.call(options)
|
44
|
+
old_annotation = old_content.match(annotation_pattern).to_s
|
45
|
+
|
46
|
+
# if there *was* no old schema info or :force was passed, we simply
|
47
|
+
# need to insert it in correct position
|
48
|
+
if old_annotation.empty? || options[:force]
|
49
|
+
magic_comments_block = Helper.magic_comments_as_string(old_content)
|
50
|
+
old_content.gsub!(Annotator::MAGIC_COMMENT_MATCHER, '')
|
51
|
+
|
52
|
+
annotation_pattern = AnnotationPatternGenerator.call(options)
|
53
|
+
old_content.sub!(annotation_pattern, '')
|
54
|
+
|
55
|
+
new_content = if %w(after bottom).include?(options[position].to_s)
|
56
|
+
magic_comments_block + (old_content.rstrip + "\n\n" + wrapped_info_block)
|
57
|
+
elsif magic_comments_block.empty?
|
58
|
+
magic_comments_block + wrapped_info_block + old_content.lstrip
|
59
|
+
else
|
60
|
+
magic_comments_block + "\n" + wrapped_info_block + old_content.lstrip
|
61
|
+
end
|
62
|
+
else
|
63
|
+
# replace the old annotation with the new one
|
64
|
+
|
65
|
+
# keep the surrounding whitespace the same
|
66
|
+
space_match = old_annotation.match(/\A(?<start>\s*).*?\n(?<end>\s*)\z/m)
|
67
|
+
new_annotation = space_match[:start] + wrapped_info_block + space_match[:end]
|
68
|
+
|
69
|
+
annotation_pattern = AnnotationPatternGenerator.call(options)
|
70
|
+
new_content = old_content.sub(annotation_pattern, new_annotation)
|
71
|
+
end
|
72
|
+
|
73
|
+
File.open(file_name, 'wb') { |f| f.puts new_content }
|
74
|
+
true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|