annotaterb 4.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![CI](https://github.com/drwl/annotaterb/actions/workflows/ci.yml/badge.svg)](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
|