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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +0 -0
  3. data/LICENSE.txt +55 -0
  4. data/README.md +91 -0
  5. data/VERSION +1 -0
  6. data/exe/annotaterb +21 -0
  7. data/lib/annotate_rb/active_record_patch.rb +9 -0
  8. data/lib/annotate_rb/commands/annotate_models.rb +22 -0
  9. data/lib/annotate_rb/commands/annotate_routes.rb +19 -0
  10. data/lib/annotate_rb/commands/print_help.rb +16 -0
  11. data/lib/annotate_rb/commands/print_version.rb +12 -0
  12. data/lib/annotate_rb/commands.rb +10 -0
  13. data/lib/annotate_rb/config_finder.rb +21 -0
  14. data/lib/annotate_rb/config_loader.rb +63 -0
  15. data/lib/annotate_rb/core.rb +23 -0
  16. data/lib/annotate_rb/eager_loader.rb +23 -0
  17. data/lib/annotate_rb/env.rb +30 -0
  18. data/lib/annotate_rb/model_annotator/annotation_pattern_generator.rb +19 -0
  19. data/lib/annotate_rb/model_annotator/annotator.rb +74 -0
  20. data/lib/annotate_rb/model_annotator/bad_model_file_error.rb +11 -0
  21. data/lib/annotate_rb/model_annotator/constants.rb +22 -0
  22. data/lib/annotate_rb/model_annotator/file_annotation_remover.rb +25 -0
  23. data/lib/annotate_rb/model_annotator/file_annotator.rb +79 -0
  24. data/lib/annotate_rb/model_annotator/file_name_resolver.rb +16 -0
  25. data/lib/annotate_rb/model_annotator/file_patterns.rb +129 -0
  26. data/lib/annotate_rb/model_annotator/helper.rb +54 -0
  27. data/lib/annotate_rb/model_annotator/model_class_getter.rb +63 -0
  28. data/lib/annotate_rb/model_annotator/model_file_annotator.rb +118 -0
  29. data/lib/annotate_rb/model_annotator/model_files_getter.rb +62 -0
  30. data/lib/annotate_rb/model_annotator/pattern_getter.rb +27 -0
  31. data/lib/annotate_rb/model_annotator/schema_info.rb +480 -0
  32. data/lib/annotate_rb/model_annotator.rb +20 -0
  33. data/lib/annotate_rb/options.rb +204 -0
  34. data/lib/annotate_rb/parser.rb +385 -0
  35. data/lib/annotate_rb/rake_bootstrapper.rb +34 -0
  36. data/lib/annotate_rb/route_annotator/annotation_processor.rb +56 -0
  37. data/lib/annotate_rb/route_annotator/annotator.rb +40 -0
  38. data/lib/annotate_rb/route_annotator/base_processor.rb +104 -0
  39. data/lib/annotate_rb/route_annotator/header_generator.rb +113 -0
  40. data/lib/annotate_rb/route_annotator/helper.rb +104 -0
  41. data/lib/annotate_rb/route_annotator/removal_processor.rb +40 -0
  42. data/lib/annotate_rb/route_annotator.rb +12 -0
  43. data/lib/annotate_rb/runner.rb +34 -0
  44. data/lib/annotate_rb/tasks/annotate_models_migrate.rake +30 -0
  45. data/lib/annotate_rb.rb +30 -0
  46. data/lib/generators/annotate_rb/USAGE +4 -0
  47. data/lib/generators/annotate_rb/install_generator.rb +15 -0
  48. data/lib/generators/annotate_rb/templates/auto_annotate_models.rake +7 -0
  49. metadata +96 -0
@@ -0,0 +1,113 @@
1
+ module AnnotateRb
2
+ module RouteAnnotator
3
+ class HeaderGenerator
4
+ PREFIX = '== Route Map'.freeze
5
+ PREFIX_MD = '## Route Map'.freeze
6
+ HEADER_ROW = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action'].freeze
7
+
8
+ class << self
9
+ def generate(options = {})
10
+ new(options, routes_map(options)).generate
11
+ end
12
+
13
+ private :new
14
+
15
+ private
16
+
17
+ def routes_map(options)
18
+ result = `rails routes`.chomp("\n").split(/\n/, -1)
19
+
20
+ # In old versions of Rake, the first line of output was the cwd. Not so
21
+ # much in newer ones. We ditch that line if it exists, and if not, we
22
+ # keep the line around.
23
+ result.shift if result.first =~ %r{^\(in \/}
24
+
25
+ ignore_routes = options[:ignore_routes]
26
+ regexp_for_ignoring_routes = ignore_routes ? /#{ignore_routes}/ : nil
27
+
28
+ # Skip routes which match given regex
29
+ # Note: it matches the complete line (route_name, path, controller/action)
30
+ if regexp_for_ignoring_routes
31
+ result.reject { |line| line =~ regexp_for_ignoring_routes }
32
+ else
33
+ result
34
+ end
35
+ end
36
+ end
37
+
38
+ def initialize(options, routes_map)
39
+ @options = options
40
+ @routes_map = routes_map
41
+ end
42
+
43
+ def generate
44
+ magic_comments_map, contents_without_magic_comments = Helper.extract_magic_comments_from_array(routes_map)
45
+
46
+ out = []
47
+
48
+ magic_comments_map.each do |magic_comment|
49
+ out << magic_comment
50
+ end
51
+ out << '' if magic_comments_map.any?
52
+
53
+ out << comment(options[:wrapper_open]) if options[:wrapper_open]
54
+
55
+ out << comment(markdown? ? PREFIX_MD : PREFIX) + timestamp_if_required
56
+ out << comment
57
+ return out if contents_without_magic_comments.size.zero?
58
+
59
+ maxs = [HEADER_ROW.map(&:size)] + contents_without_magic_comments[1..-1].map { |line| line.split.map(&:size) }
60
+
61
+ if markdown?
62
+ max = maxs.map(&:max).compact.max
63
+
64
+ out << comment(content(HEADER_ROW, maxs))
65
+ out << comment(content(['-' * max, '-' * max, '-' * max, '-' * max], maxs))
66
+ else
67
+ out << comment(content(contents_without_magic_comments[0], maxs))
68
+ end
69
+
70
+ out += contents_without_magic_comments[1..-1].map { |line| comment(content(markdown? ? line.split(' ') : line, maxs)) }
71
+ out << comment(options[:wrapper_close]) if options[:wrapper_close]
72
+
73
+ out
74
+ end
75
+
76
+ private
77
+
78
+ attr_reader :options, :routes_map
79
+
80
+ def comment(row = '')
81
+ if row == ''
82
+ '#'
83
+ else
84
+ "# #{row}"
85
+ end
86
+ end
87
+
88
+ def content(line, maxs)
89
+ return line.rstrip unless markdown?
90
+
91
+ line.each_with_index.map { |elem, index| format_line_element(elem, maxs, index) }.join(' | ')
92
+ end
93
+
94
+ def format_line_element(elem, maxs, index)
95
+ min_length = maxs.map { |arr| arr[index] }.max || 0
96
+ format("%-#{min_length}.#{min_length}s", elem.tr('|', '-'))
97
+ end
98
+
99
+ def markdown?
100
+ options[:format_markdown]
101
+ end
102
+
103
+ def timestamp_if_required(time = Time.now)
104
+ if options[:timestamp]
105
+ time_formatted = time.strftime('%Y-%m-%d %H:%M')
106
+ " (Updated #{time_formatted})"
107
+ else
108
+ ''
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module RouteAnnotator
5
+ module Helper
6
+ MAGIC_COMMENT_MATCHER = Regexp.new(/(^#\s*encoding:.*)|(^# coding:.*)|(^# -\*- coding:.*)|(^# -\*- encoding\s?:.*)|(^#\s*frozen_string_literal:.+)|(^# -\*- frozen_string_literal\s*:.+-\*-)/).freeze
7
+
8
+ class << self
9
+ def routes_file_exist?
10
+ File.exist?(routes_file)
11
+ end
12
+
13
+ def routes_file
14
+ @routes_rb ||= File.join('config', 'routes.rb')
15
+ end
16
+
17
+ def strip_on_removal(content, header_position)
18
+ if header_position == :before
19
+ content.shift while content.first == ''
20
+ elsif header_position == :after
21
+ content.pop while content.last == ''
22
+ end
23
+
24
+ # Make sure we end on a trailing newline.
25
+ content << '' unless content.last == ''
26
+
27
+ # TODO: If the user buried it in the middle, we should probably see about
28
+ # TODO: preserving a single line of space between the content above and
29
+ # TODO: below...
30
+ content
31
+ end
32
+
33
+ def rewrite_contents(existing_text, new_text)
34
+ if existing_text == new_text
35
+ false
36
+ else
37
+ File.open(routes_file, 'wb') { |f| f.puts(new_text) }
38
+ true
39
+ end
40
+ end
41
+
42
+ # @param [Array<String>] content
43
+ # @return [Array<String>] all found magic comments
44
+ # @return [Array<String>] content without magic comments
45
+ def extract_magic_comments_from_array(content_array)
46
+ magic_comments = []
47
+ new_content = []
48
+
49
+ content_array.each do |row|
50
+ if row =~ MAGIC_COMMENT_MATCHER
51
+ magic_comments << row.strip
52
+ else
53
+ new_content << row
54
+ end
55
+ end
56
+
57
+ [magic_comments, new_content]
58
+ end
59
+
60
+ # TODO: write the method doc using ruby rdoc formats
61
+ # This method returns an array of 'real_content' and 'header_position'.
62
+ # 'header_position' will either be :before, :after, or
63
+ # a number. If the number is > 0, the
64
+ # annotation was found somewhere in the
65
+ # middle of the file. If the number is
66
+ # zero, no annotation was found.
67
+ def strip_annotations(content)
68
+ real_content = []
69
+ mode = :content
70
+ header_position = 0
71
+
72
+ content.split(/\n/, -1).each_with_index do |line, line_number|
73
+ if mode == :header && line !~ /\s*#/
74
+ mode = :content
75
+ real_content << line unless line.blank?
76
+ elsif mode == :content
77
+ if line =~ /^\s*#\s*== Route.*$/
78
+ header_position = line_number + 1 # index start's at 0
79
+ mode = :header
80
+ else
81
+ real_content << line
82
+ end
83
+ end
84
+ end
85
+
86
+ real_content_and_header_position(real_content, header_position)
87
+ end
88
+
89
+ def real_content_and_header_position(real_content, header_position)
90
+ # By default assume the annotation was found in the middle of the file
91
+
92
+ # ... unless we have evidence it was at the beginning ...
93
+ return real_content, :before if header_position == 1
94
+
95
+ # ... or that it was at the end.
96
+ return real_content, :after if header_position >= real_content.count
97
+
98
+ # and the default
99
+ return real_content, header_position
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module provides methods for annotating config/routes.rb.
4
+ module AnnotateRb
5
+ module RouteAnnotator
6
+ # This class is abstract class of classes adding and removing annotation to config/routes.rb.
7
+ class RemovalProcessor < BaseProcessor
8
+ # @return [String]
9
+ def execute
10
+ if routes_file_exist?
11
+ if update
12
+ "Annotations were removed from #{routes_file}."
13
+ else
14
+ "#{routes_file} was not changed (Annotation did not exist)."
15
+ end
16
+ else
17
+ "#{routes_file} could not be found."
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def generate_new_content_array(content, header_position)
24
+ if header_position == :before
25
+ content.shift while content.first == ''
26
+ elsif header_position == :after
27
+ content.pop while content.last == ''
28
+ end
29
+
30
+ # Make sure we end on a trailing newline.
31
+ content << '' unless content.last == ''
32
+
33
+ # TODO: If the user buried it in the middle, we should probably see about
34
+ # TODO: preserving a single line of space between the content above and
35
+ # TODO: below...
36
+ content
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module RouteAnnotator
5
+ autoload :Annotator, 'annotate_rb/route_annotator/annotator'
6
+ autoload :Helper, 'annotate_rb/route_annotator/helper'
7
+ autoload :HeaderGenerator, 'annotate_rb/route_annotator/header_generator'
8
+ autoload :BaseProcessor, 'annotate_rb/route_annotator/base_processor'
9
+ autoload :AnnotationProcessor, 'annotate_rb/route_annotator/annotation_processor'
10
+ autoload :RemovalProcessor, 'annotate_rb/route_annotator/removal_processor'
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ class Runner
5
+ class << self
6
+ def run(args)
7
+ new.run(args)
8
+ end
9
+ end
10
+
11
+ def initialize
12
+
13
+ end
14
+
15
+ def run(args)
16
+ _original_args = args.dup
17
+
18
+ config_file_options = ConfigLoader.load_config
19
+ parsed_options = Parser.parse(args)
20
+
21
+ options = config_file_options.merge(parsed_options)
22
+
23
+ @options = Options.from(options, {})
24
+ AnnotateRb::RakeBootstrapper.call(@options)
25
+
26
+ if @options[:command]
27
+ @options[:command].call(@options)
28
+ else
29
+ # TODO
30
+ raise "Didn't specify a command"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ # These tasks are added to the project if you install annotate as a Rails plugin.
2
+ # (They are not used to build annotate itself.)
3
+
4
+ # Append annotations to Rake tasks for ActiveRecord, so annotate automatically gets
5
+ # run after doing db:migrate.
6
+
7
+ # Migration tasks are tasks that we'll "hook" into
8
+ migration_tasks = %w(db:migrate db:migrate:up db:migrate:down db:migrate:reset db:migrate:redo db:rollback)
9
+ if defined?(Rails::Application) && Rails.version.split('.').first.to_i >= 6
10
+ require 'active_record'
11
+
12
+ databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
13
+
14
+ # If there's multiple databases, this appends database specific rake tasks to `migration_tasks`
15
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |database_name|
16
+ migration_tasks.concat(%w(db:migrate db:migrate:up db:migrate:down).map { |task| "#{task}:#{database_name}" })
17
+ end
18
+ end
19
+
20
+ migration_tasks.each do |task|
21
+ next unless Rake::Task.task_defined?(task)
22
+
23
+ Rake::Task[task].enhance do # This block is ran after `task` completes
24
+ task_name = Rake.application.top_level_tasks.last # The name of the task that was run, e.g. "db:migrate"
25
+
26
+ Rake::Task[task_name].enhance do
27
+ ::AnnotateRb::Runner.run(['models'])
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record'
4
+ require 'active_support'
5
+
6
+ # Helper.fallback depends on this being required because it adds #present? to nil
7
+ require 'active_support/core_ext/object/blank'
8
+ require 'active_support/core_ext/class/subclasses'
9
+ require 'active_support/core_ext/string/inflections'
10
+
11
+ require 'rake'
12
+
13
+ require 'annotate_rb/active_record_patch'
14
+
15
+ require_relative 'annotate_rb/core'
16
+ require_relative 'annotate_rb/commands'
17
+ require_relative 'annotate_rb/parser'
18
+ require_relative 'annotate_rb/runner'
19
+ require_relative 'annotate_rb/route_annotator'
20
+ require_relative 'annotate_rb/model_annotator'
21
+ require_relative 'annotate_rb/env'
22
+ require_relative 'annotate_rb/options'
23
+ require_relative 'annotate_rb/eager_loader'
24
+ require_relative 'annotate_rb/rake_bootstrapper'
25
+ require_relative 'annotate_rb/config_finder'
26
+ require_relative 'annotate_rb/config_loader'
27
+
28
+ module AnnotateRb
29
+
30
+ end
@@ -0,0 +1,4 @@
1
+ Add a .rake file that automatically annotates models when you do a db:migrate
2
+ in development mode:
3
+
4
+ rails generate annotaterb:install
@@ -0,0 +1,15 @@
1
+ require 'annotate_rb'
2
+
3
+ module AnnotateRb
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ desc 'Copy annotaterb rakefiles for automatic annotation of models'
7
+ source_root File.expand_path('templates', __dir__)
8
+
9
+ def copy_tasks
10
+ # Copies the rake task into Rails project's lib/tasks directory
11
+ template 'auto_annotate_models.rake', 'lib/tasks/auto_annotate_models.rake'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ # This rake task was added by annotate_rb gem.
2
+
3
+ if Rails.env.development?
4
+ require 'annotate_rb'
5
+
6
+ AnnotateRb::Core.load_rake_tasks
7
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: annotaterb
3
+ version: !ruby/object:Gem::Version
4
+ version: 4.0.0.beta.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrew W. Lee
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-04-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Annotates Rails/ActiveRecord Models, routes, fixtures, and others based
14
+ on the database schema.
15
+ email:
16
+ - git@drewlee.com
17
+ executables:
18
+ - annotaterb
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - CHANGELOG.md
23
+ - LICENSE.txt
24
+ - README.md
25
+ - VERSION
26
+ - exe/annotaterb
27
+ - lib/annotate_rb.rb
28
+ - lib/annotate_rb/active_record_patch.rb
29
+ - lib/annotate_rb/commands.rb
30
+ - lib/annotate_rb/commands/annotate_models.rb
31
+ - lib/annotate_rb/commands/annotate_routes.rb
32
+ - lib/annotate_rb/commands/print_help.rb
33
+ - lib/annotate_rb/commands/print_version.rb
34
+ - lib/annotate_rb/config_finder.rb
35
+ - lib/annotate_rb/config_loader.rb
36
+ - lib/annotate_rb/core.rb
37
+ - lib/annotate_rb/eager_loader.rb
38
+ - lib/annotate_rb/env.rb
39
+ - lib/annotate_rb/model_annotator.rb
40
+ - lib/annotate_rb/model_annotator/annotation_pattern_generator.rb
41
+ - lib/annotate_rb/model_annotator/annotator.rb
42
+ - lib/annotate_rb/model_annotator/bad_model_file_error.rb
43
+ - lib/annotate_rb/model_annotator/constants.rb
44
+ - lib/annotate_rb/model_annotator/file_annotation_remover.rb
45
+ - lib/annotate_rb/model_annotator/file_annotator.rb
46
+ - lib/annotate_rb/model_annotator/file_name_resolver.rb
47
+ - lib/annotate_rb/model_annotator/file_patterns.rb
48
+ - lib/annotate_rb/model_annotator/helper.rb
49
+ - lib/annotate_rb/model_annotator/model_class_getter.rb
50
+ - lib/annotate_rb/model_annotator/model_file_annotator.rb
51
+ - lib/annotate_rb/model_annotator/model_files_getter.rb
52
+ - lib/annotate_rb/model_annotator/pattern_getter.rb
53
+ - lib/annotate_rb/model_annotator/schema_info.rb
54
+ - lib/annotate_rb/options.rb
55
+ - lib/annotate_rb/parser.rb
56
+ - lib/annotate_rb/rake_bootstrapper.rb
57
+ - lib/annotate_rb/route_annotator.rb
58
+ - lib/annotate_rb/route_annotator/annotation_processor.rb
59
+ - lib/annotate_rb/route_annotator/annotator.rb
60
+ - lib/annotate_rb/route_annotator/base_processor.rb
61
+ - lib/annotate_rb/route_annotator/header_generator.rb
62
+ - lib/annotate_rb/route_annotator/helper.rb
63
+ - lib/annotate_rb/route_annotator/removal_processor.rb
64
+ - lib/annotate_rb/runner.rb
65
+ - lib/annotate_rb/tasks/annotate_models_migrate.rake
66
+ - lib/generators/annotate_rb/USAGE
67
+ - lib/generators/annotate_rb/install_generator.rb
68
+ - lib/generators/annotate_rb/templates/auto_annotate_models.rake
69
+ homepage: https://github.com/drwl/annotaterb
70
+ licenses:
71
+ - BSD-2-Clause
72
+ metadata:
73
+ homepage_uri: https://github.com/drwl/annotaterb
74
+ source_code_uri: https://github.com/drwl/annotaterb
75
+ changelog_uri: https://github.com/drwl/annotaterb/blob/master/CHANGELOG.md
76
+ bug_tracker_uri: https://github.com/drwl/annotaterb/issues
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: 2.7.0
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">"
89
+ - !ruby/object:Gem::Version
90
+ version: 1.3.1
91
+ requirements: []
92
+ rubygems_version: 3.2.33
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: A gem for generating annotations for Rails projects.
96
+ test_files: []