annotate 3.1.1 → 3.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 +4 -4
- data/README.md +2 -2
- data/annotate.gemspec +6 -3
- data/lib/annotate/annotate_models/file_patterns.rb +127 -0
- data/lib/annotate/annotate_models.rb +71 -160
- data/lib/annotate/annotate_routes/header_generator.rb +113 -0
- data/lib/annotate/annotate_routes.rb +2 -77
- data/lib/annotate/version.rb +1 -1
- data/lib/tasks/annotate_models_migrate.rake +14 -1
- data/lib/tasks/annotate_routes.rake +7 -1
- metadata +10 -8
checksums.yaml CHANGED
| @@ -1,7 +1,7 @@ | |
| 1 1 | --- |
| 2 2 | SHA256: |
| 3 | - metadata.gz: |
| 4 | - data.tar.gz: |
| 3 | + metadata.gz: a49c914d3c2486e109b4a499b71fe905c8534e21b1d44cee15056a3066468e2e |
| 4 | + data.tar.gz: 383445b9f5c13d02da7190a009ed974b5f88b41fe88f156814e743b4f7ec99d2 |
| 5 5 | SHA512: |
| 6 | - metadata.gz: |
| 7 | - data.tar.gz: |
| 6 | + metadata.gz: 70b926aba8e1ba91ccf506cfdabb94016efaaf788beaf6bdc0691fb6ad30edf57b794842f93f9860e1f82fa38c2934530e6fc0940f88ce4b47a909da8456ddd0 |
| 7 | + data.tar.gz: bf94c7d38b3a11ddc658947a7c1f2164854070a5707f56c148bcbdd5495b88c270ee0f0e587b07c0c421724b40059f110c808a8f3af90e68a86c5e249dfb1fa1 |
data/README.md CHANGED
| @@ -104,8 +104,8 @@ Into environment gems from Github checkout: | |
| 104 104 | |
| 105 105 | git clone https://github.com/ctran/annotate_models.git annotate_models |
| 106 106 | cd annotate_models |
| 107 | - rake |
| 108 | - gem install |
| 107 | + rake gem |
| 108 | + gem install dist/annotate-*.gem |
| 109 109 | |
| 110 110 | ## Usage |
| 111 111 | |
data/annotate.gemspec CHANGED
| @@ -15,7 +15,7 @@ Gem::Specification.new do |s| | |
| 15 15 | s.executables = ['annotate'] |
| 16 16 | s.extra_rdoc_files = ['README.md', 'CHANGELOG.md'] |
| 17 17 | s.files = `git ls-files -z LICENSE.txt *.md *.gemspec bin lib`.split("\x0") |
| 18 | - s.homepage = ' |
| 18 | + s.homepage = 'https://github.com/ctran/annotate_models' |
| 19 19 | s.licenses = ['Ruby'] |
| 20 20 | s.require_paths = ['lib'] |
| 21 21 | s.rubygems_version = '2.1.11' |
| @@ -23,7 +23,10 @@ Gem::Specification.new do |s| | |
| 23 23 | |
| 24 24 | s.specification_version = 4 if s.respond_to? :specification_version |
| 25 25 | s.add_runtime_dependency(%q<rake>, '>= 10.4', '< 14.0') |
| 26 | - s.add_runtime_dependency(%q<activerecord>, ['>= 3.2', '< |
| 26 | + s.add_runtime_dependency(%q<activerecord>, ['>= 3.2', '< 8.0']) |
| 27 27 | |
| 28 | - s.metadata = { |
| 28 | + s.metadata = { |
| 29 | + "bug_tracker_uri" => "https://github.com/ctran/annotate_models/issues/", |
| 30 | + "source_code_uri" => "https://github.com/ctran/annotate_models.git" |
| 31 | + } |
| 29 32 | end |
| @@ -0,0 +1,127 @@ | |
| 1 | + module AnnotateModels |
| 2 | + # This module provides module method to get file paths. |
| 3 | + module FilePatterns |
| 4 | + # Controller files |
| 5 | + CONTROLLER_DIR = File.join('app', 'controllers') |
| 6 | + |
| 7 | + # Active admin registry files |
| 8 | + ACTIVEADMIN_DIR = File.join('app', 'admin') |
| 9 | + |
| 10 | + # Helper files |
| 11 | + HELPER_DIR = File.join('app', 'helpers') |
| 12 | + |
| 13 | + # File.join for windows reverse bar compat? |
| 14 | + # I dont use windows, can`t test |
| 15 | + UNIT_TEST_DIR = File.join('test', 'unit') |
| 16 | + MODEL_TEST_DIR = File.join('test', 'models') # since rails 4.0 |
| 17 | + SPEC_MODEL_DIR = File.join('spec', 'models') |
| 18 | + |
| 19 | + FIXTURE_TEST_DIR = File.join('test', 'fixtures') |
| 20 | + FIXTURE_SPEC_DIR = File.join('spec', 'fixtures') |
| 21 | + |
| 22 | + # Other test files |
| 23 | + CONTROLLER_TEST_DIR = File.join('test', 'controllers') |
| 24 | + CONTROLLER_SPEC_DIR = File.join('spec', 'controllers') |
| 25 | + REQUEST_SPEC_DIR = File.join('spec', 'requests') |
| 26 | + ROUTING_SPEC_DIR = File.join('spec', 'routing') |
| 27 | + |
| 28 | + # Object Daddy http://github.com/flogic/object_daddy/tree/master |
| 29 | + EXEMPLARS_TEST_DIR = File.join('test', 'exemplars') |
| 30 | + EXEMPLARS_SPEC_DIR = File.join('spec', 'exemplars') |
| 31 | + |
| 32 | + # Machinist http://github.com/notahat/machinist |
| 33 | + BLUEPRINTS_TEST_DIR = File.join('test', 'blueprints') |
| 34 | + BLUEPRINTS_SPEC_DIR = File.join('spec', 'blueprints') |
| 35 | + |
| 36 | + # Factory Bot https://github.com/thoughtbot/factory_bot |
| 37 | + FACTORY_BOT_TEST_DIR = File.join('test', 'factories') |
| 38 | + FACTORY_BOT_SPEC_DIR = File.join('spec', 'factories') |
| 39 | + |
| 40 | + # Fabrication https://github.com/paulelliott/fabrication.git |
| 41 | + FABRICATORS_TEST_DIR = File.join('test', 'fabricators') |
| 42 | + FABRICATORS_SPEC_DIR = File.join('spec', 'fabricators') |
| 43 | + |
| 44 | + # Serializers https://github.com/rails-api/active_model_serializers |
| 45 | + SERIALIZERS_DIR = File.join('app', 'serializers') |
| 46 | + SERIALIZERS_TEST_DIR = File.join('test', 'serializers') |
| 47 | + SERIALIZERS_SPEC_DIR = File.join('spec', 'serializers') |
| 48 | + |
| 49 | + class << self |
| 50 | + def generate(root_directory, pattern_type, options) |
| 51 | + case pattern_type |
| 52 | + when 'test' then test_files(root_directory) |
| 53 | + when 'fixture' then fixture_files(root_directory) |
| 54 | + when 'scaffold' then scaffold_files(root_directory) |
| 55 | + when 'factory' then factory_files(root_directory) |
| 56 | + when 'serializer' then serialize_files(root_directory) |
| 57 | + when 'additional_file_patterns' |
| 58 | + [options[:additional_file_patterns] || []].flatten |
| 59 | + when 'controller' |
| 60 | + [File.join(root_directory, CONTROLLER_DIR, '%PLURALIZED_MODEL_NAME%_controller.rb')] |
| 61 | + when 'admin' |
| 62 | + [ |
| 63 | + File.join(root_directory, ACTIVEADMIN_DIR, '%MODEL_NAME%.rb'), |
| 64 | + File.join(root_directory, ACTIVEADMIN_DIR, '%PLURALIZED_MODEL_NAME%.rb') |
| 65 | + ] |
| 66 | + when 'helper' |
| 67 | + [File.join(root_directory, HELPER_DIR, '%PLURALIZED_MODEL_NAME%_helper.rb')] |
| 68 | + else |
| 69 | + [] |
| 70 | + end |
| 71 | + end |
| 72 | + |
| 73 | + private |
| 74 | + |
| 75 | + def test_files(root_directory) |
| 76 | + [ |
| 77 | + File.join(root_directory, UNIT_TEST_DIR, '%MODEL_NAME%_test.rb'), |
| 78 | + File.join(root_directory, MODEL_TEST_DIR, '%MODEL_NAME%_test.rb'), |
| 79 | + File.join(root_directory, SPEC_MODEL_DIR, '%MODEL_NAME%_spec.rb') |
| 80 | + ] |
| 81 | + end |
| 82 | + |
| 83 | + def fixture_files(root_directory) |
| 84 | + [ |
| 85 | + File.join(root_directory, FIXTURE_TEST_DIR, '%TABLE_NAME%.yml'), |
| 86 | + File.join(root_directory, FIXTURE_SPEC_DIR, '%TABLE_NAME%.yml'), |
| 87 | + File.join(root_directory, FIXTURE_TEST_DIR, '%PLURALIZED_MODEL_NAME%.yml'), |
| 88 | + File.join(root_directory, FIXTURE_SPEC_DIR, '%PLURALIZED_MODEL_NAME%.yml') |
| 89 | + ] |
| 90 | + end |
| 91 | + |
| 92 | + def scaffold_files(root_directory) |
| 93 | + [ |
| 94 | + File.join(root_directory, CONTROLLER_TEST_DIR, '%PLURALIZED_MODEL_NAME%_controller_test.rb'), |
| 95 | + File.join(root_directory, CONTROLLER_SPEC_DIR, '%PLURALIZED_MODEL_NAME%_controller_spec.rb'), |
| 96 | + File.join(root_directory, REQUEST_SPEC_DIR, '%PLURALIZED_MODEL_NAME%_spec.rb'), |
| 97 | + File.join(root_directory, ROUTING_SPEC_DIR, '%PLURALIZED_MODEL_NAME%_routing_spec.rb') |
| 98 | + ] |
| 99 | + end |
| 100 | + |
| 101 | + def factory_files(root_directory) |
| 102 | + [ |
| 103 | + File.join(root_directory, EXEMPLARS_TEST_DIR, '%MODEL_NAME%_exemplar.rb'), |
| 104 | + File.join(root_directory, EXEMPLARS_SPEC_DIR, '%MODEL_NAME%_exemplar.rb'), |
| 105 | + File.join(root_directory, BLUEPRINTS_TEST_DIR, '%MODEL_NAME%_blueprint.rb'), |
| 106 | + File.join(root_directory, BLUEPRINTS_SPEC_DIR, '%MODEL_NAME%_blueprint.rb'), |
| 107 | + File.join(root_directory, FACTORY_BOT_TEST_DIR, '%MODEL_NAME%_factory.rb'), # (old style) |
| 108 | + File.join(root_directory, FACTORY_BOT_SPEC_DIR, '%MODEL_NAME%_factory.rb'), # (old style) |
| 109 | + File.join(root_directory, FACTORY_BOT_TEST_DIR, '%TABLE_NAME%.rb'), # (new style) |
| 110 | + File.join(root_directory, FACTORY_BOT_SPEC_DIR, '%TABLE_NAME%.rb'), # (new style) |
| 111 | + File.join(root_directory, FACTORY_BOT_TEST_DIR, '%PLURALIZED_MODEL_NAME%.rb'), # (new style) |
| 112 | + File.join(root_directory, FACTORY_BOT_SPEC_DIR, '%PLURALIZED_MODEL_NAME%.rb'), # (new style) |
| 113 | + File.join(root_directory, FABRICATORS_TEST_DIR, '%MODEL_NAME%_fabricator.rb'), |
| 114 | + File.join(root_directory, FABRICATORS_SPEC_DIR, '%MODEL_NAME%_fabricator.rb') |
| 115 | + ] |
| 116 | + end |
| 117 | + |
| 118 | + def serialize_files(root_directory) |
| 119 | + [ |
| 120 | + File.join(root_directory, SERIALIZERS_DIR, '%MODEL_NAME%_serializer.rb'), |
| 121 | + File.join(root_directory, SERIALIZERS_TEST_DIR, '%MODEL_NAME%_serializer_test.rb'), |
| 122 | + File.join(root_directory, SERIALIZERS_SPEC_DIR, '%MODEL_NAME%_serializer_spec.rb') |
| 123 | + ] |
| 124 | + end |
| 125 | + end |
| 126 | + end |
| 127 | + end |
| @@ -3,6 +3,7 @@ | |
| 3 3 | require 'bigdecimal' |
| 4 4 | |
| 5 5 | require 'annotate/constants' |
| 6 | + require_relative 'annotate_models/file_patterns' |
| 6 7 | |
| 7 8 | module AnnotateModels |
| 8 9 | # Annotate Models plugin use this header |
| @@ -16,50 +17,6 @@ module AnnotateModels | |
| 16 17 | |
| 17 18 | MATCHED_TYPES = %w(test fixture factory serializer scaffold controller helper).freeze |
| 18 19 | |
| 19 | - # File.join for windows reverse bar compat? |
| 20 | - # I dont use windows, can`t test |
| 21 | - UNIT_TEST_DIR = File.join('test', "unit") |
| 22 | - MODEL_TEST_DIR = File.join('test', "models") # since rails 4.0 |
| 23 | - SPEC_MODEL_DIR = File.join('spec', "models") |
| 24 | - FIXTURE_TEST_DIR = File.join('test', "fixtures") |
| 25 | - FIXTURE_SPEC_DIR = File.join('spec', "fixtures") |
| 26 | - |
| 27 | - # Other test files |
| 28 | - CONTROLLER_TEST_DIR = File.join('test', "controllers") |
| 29 | - CONTROLLER_SPEC_DIR = File.join('spec', "controllers") |
| 30 | - REQUEST_SPEC_DIR = File.join('spec', "requests") |
| 31 | - ROUTING_SPEC_DIR = File.join('spec', "routing") |
| 32 | - |
| 33 | - # Object Daddy http://github.com/flogic/object_daddy/tree/master |
| 34 | - EXEMPLARS_TEST_DIR = File.join('test', "exemplars") |
| 35 | - EXEMPLARS_SPEC_DIR = File.join('spec', "exemplars") |
| 36 | - |
| 37 | - # Machinist http://github.com/notahat/machinist |
| 38 | - BLUEPRINTS_TEST_DIR = File.join('test', "blueprints") |
| 39 | - BLUEPRINTS_SPEC_DIR = File.join('spec', "blueprints") |
| 40 | - |
| 41 | - # Factory Bot https://github.com/thoughtbot/factory_bot |
| 42 | - FACTORY_BOT_TEST_DIR = File.join('test', "factories") |
| 43 | - FACTORY_BOT_SPEC_DIR = File.join('spec', "factories") |
| 44 | - |
| 45 | - # Fabrication https://github.com/paulelliott/fabrication.git |
| 46 | - FABRICATORS_TEST_DIR = File.join('test', "fabricators") |
| 47 | - FABRICATORS_SPEC_DIR = File.join('spec', "fabricators") |
| 48 | - |
| 49 | - # Serializers https://github.com/rails-api/active_model_serializers |
| 50 | - SERIALIZERS_DIR = File.join('app', "serializers") |
| 51 | - SERIALIZERS_TEST_DIR = File.join('test', "serializers") |
| 52 | - SERIALIZERS_SPEC_DIR = File.join('spec', "serializers") |
| 53 | - |
| 54 | - # Controller files |
| 55 | - CONTROLLER_DIR = File.join('app', "controllers") |
| 56 | - |
| 57 | - # Active admin registry files |
| 58 | - ACTIVEADMIN_DIR = File.join('app', "admin") |
| 59 | - |
| 60 | - # Helper files |
| 61 | - HELPER_DIR = File.join('app', "helpers") |
| 62 | - |
| 63 20 | # Don't show limit (#) on these column types |
| 64 21 | # Example: show "integer" instead of "integer(4)" |
| 65 22 | NO_LIMIT_COL_TYPES = %w(integer bigint boolean).freeze |
| @@ -110,82 +67,24 @@ module AnnotateModels | |
| 110 67 | |
| 111 68 | attr_writer :root_dir |
| 112 69 | |
| 113 | - def |
| 114 | - [ |
| 115 | - |
| 116 | - File.join(root_directory, MODEL_TEST_DIR, "%MODEL_NAME%_test.rb"), |
| 117 | - File.join(root_directory, SPEC_MODEL_DIR, "%MODEL_NAME%_spec.rb") |
| 118 | - ] |
| 119 | - end |
| 120 | - |
| 121 | - def fixture_files(root_directory) |
| 122 | - [ |
| 123 | - File.join(root_directory, FIXTURE_TEST_DIR, "%TABLE_NAME%.yml"), |
| 124 | - File.join(root_directory, FIXTURE_SPEC_DIR, "%TABLE_NAME%.yml"), |
| 125 | - File.join(root_directory, FIXTURE_TEST_DIR, "%PLURALIZED_MODEL_NAME%.yml"), |
| 126 | - File.join(root_directory, FIXTURE_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.yml") |
| 127 | - ] |
| 128 | - end |
| 129 | - |
| 130 | - def scaffold_files(root_directory) |
| 131 | - [ |
| 132 | - File.join(root_directory, CONTROLLER_TEST_DIR, "%PLURALIZED_MODEL_NAME%_controller_test.rb"), |
| 133 | - File.join(root_directory, CONTROLLER_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_controller_spec.rb"), |
| 134 | - File.join(root_directory, REQUEST_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_spec.rb"), |
| 135 | - File.join(root_directory, ROUTING_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_routing_spec.rb") |
| 136 | - ] |
| 137 | - end |
| 138 | - |
| 139 | - def factory_files(root_directory) |
| 140 | - [ |
| 141 | - File.join(root_directory, EXEMPLARS_TEST_DIR, "%MODEL_NAME%_exemplar.rb"), |
| 142 | - File.join(root_directory, EXEMPLARS_SPEC_DIR, "%MODEL_NAME%_exemplar.rb"), |
| 143 | - File.join(root_directory, BLUEPRINTS_TEST_DIR, "%MODEL_NAME%_blueprint.rb"), |
| 144 | - File.join(root_directory, BLUEPRINTS_SPEC_DIR, "%MODEL_NAME%_blueprint.rb"), |
| 145 | - File.join(root_directory, FACTORY_BOT_TEST_DIR, "%MODEL_NAME%_factory.rb"), # (old style) |
| 146 | - File.join(root_directory, FACTORY_BOT_SPEC_DIR, "%MODEL_NAME%_factory.rb"), # (old style) |
| 147 | - File.join(root_directory, FACTORY_BOT_TEST_DIR, "%TABLE_NAME%.rb"), # (new style) |
| 148 | - File.join(root_directory, FACTORY_BOT_SPEC_DIR, "%TABLE_NAME%.rb"), # (new style) |
| 149 | - File.join(root_directory, FACTORY_BOT_TEST_DIR, "%PLURALIZED_MODEL_NAME%.rb"), # (new style) |
| 150 | - File.join(root_directory, FACTORY_BOT_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.rb"), # (new style) |
| 151 | - File.join(root_directory, FABRICATORS_TEST_DIR, "%MODEL_NAME%_fabricator.rb"), |
| 152 | - File.join(root_directory, FABRICATORS_SPEC_DIR, "%MODEL_NAME%_fabricator.rb") |
| 153 | - ] |
| 154 | - end |
| 155 | - |
| 156 | - def serialize_files(root_directory) |
| 157 | - [ |
| 158 | - File.join(root_directory, SERIALIZERS_DIR, "%MODEL_NAME%_serializer.rb"), |
| 159 | - File.join(root_directory, SERIALIZERS_TEST_DIR, "%MODEL_NAME%_serializer_test.rb"), |
| 160 | - File.join(root_directory, SERIALIZERS_SPEC_DIR, "%MODEL_NAME%_serializer_spec.rb") |
| 161 | - ] |
| 162 | - end |
| 70 | + def skip_subdirectory_model_load |
| 71 | + # This option is set in options[:skip_subdirectory_model_load] |
| 72 | + # and stops the get_loaded_model method from loading a model from a subdir |
| 163 73 | |
| 164 | - |
| 165 | - |
| 166 | - when 'test' then test_files(root_directory) |
| 167 | - when 'fixture' then fixture_files(root_directory) |
| 168 | - when 'scaffold' then scaffold_files(root_directory) |
| 169 | - when 'factory' then factory_files(root_directory) |
| 170 | - when 'serializer' then serialize_files(root_directory) |
| 171 | - when 'additional_file_patterns' |
| 172 | - [options[:additional_file_patterns] || []].flatten |
| 173 | - when 'controller' |
| 174 | - [File.join(root_directory, CONTROLLER_DIR, "%PLURALIZED_MODEL_NAME%_controller.rb")] |
| 175 | - when 'admin' |
| 176 | - [File.join(root_directory, ACTIVEADMIN_DIR, "%MODEL_NAME%.rb")] |
| 177 | - when 'helper' |
| 178 | - [File.join(root_directory, HELPER_DIR, "%PLURALIZED_MODEL_NAME%_helper.rb")] |
| 74 | + if @skip_subdirectory_model_load.blank? |
| 75 | + false |
| 179 76 | else |
| 180 | - |
| 77 | + @skip_subdirectory_model_load |
| 181 78 | end |
| 182 79 | end |
| 183 80 | |
| 81 | + attr_writer :skip_subdirectory_model_load |
| 82 | + |
| 184 83 | def get_patterns(options, pattern_types = []) |
| 185 84 | current_patterns = [] |
| 186 85 | root_dir.each do |root_directory| |
| 187 86 | Array(pattern_types).each do |pattern_type| |
| 188 | - patterns = |
| 87 | + patterns = FilePatterns.generate(root_directory, pattern_type, options) |
| 189 88 | |
| 190 89 | current_patterns += if pattern_type.to_sym == :additional_file_patterns |
| 191 90 | patterns |
| @@ -249,52 +148,13 @@ module AnnotateModels | |
| 249 148 | cols = columns(klass, options) |
| 250 149 | cols.each do |col| |
| 251 150 | col_type = get_col_type(col) |
| 252 | - attrs = |
| 253 | - attrs << "default(#{schema_default(klass, col)})" unless col.default.nil? || hide_default?(col_type, options) |
| 254 | - attrs << 'unsigned' if col.respond_to?(:unsigned?) && col.unsigned? |
| 255 | - attrs << 'not null' unless col.null |
| 256 | - attrs << 'primary key' if klass.primary_key && (klass.primary_key.is_a?(Array) ? klass.primary_key.collect(&:to_sym).include?(col.name.to_sym) : col.name.to_sym == klass.primary_key.to_sym) |
| 257 | - |
| 258 | - if col_type == 'decimal' |
| 259 | - col_type << "(#{col.precision}, #{col.scale})" |
| 260 | - elsif !%w[spatial geometry geography].include?(col_type) |
| 261 | - if col.limit && !options[:format_yard] |
| 262 | - if col.limit.is_a? Array |
| 263 | - attrs << "(#{col.limit.join(', ')})" |
| 264 | - else |
| 265 | - col_type << "(#{col.limit})" unless hide_limit?(col_type, options) |
| 266 | - end |
| 267 | - end |
| 268 | - end |
| 269 | - |
| 270 | - # Check out if we got an array column |
| 271 | - attrs << 'is an Array' if col.respond_to?(:array) && col.array |
| 272 | - |
| 273 | - # Check out if we got a geometric column |
| 274 | - # and print the type and SRID |
| 275 | - if col.respond_to?(:geometry_type) |
| 276 | - attrs << "#{col.geometry_type}, #{col.srid}" |
| 277 | - elsif col.respond_to?(:geometric_type) && col.geometric_type.present? |
| 278 | - attrs << "#{col.geometric_type.to_s.downcase}, #{col.srid}" |
| 279 | - end |
| 280 | - |
| 281 | - # Check if the column has indices and print "indexed" if true |
| 282 | - # If the index includes another column, print it too. |
| 283 | - if options[:simple_indexes] && klass.table_exists?# Check out if this column is indexed |
| 284 | - indices = retrieve_indexes_from_table(klass) |
| 285 | - if indices = indices.select { |ind| ind.columns.include? col.name } |
| 286 | - indices.sort_by(&:name).each do |ind| |
| 287 | - next if ind.columns.is_a?(String) |
| 288 | - ind = ind.columns.reject! { |i| i == col.name } |
| 289 | - attrs << (ind.empty? ? "indexed" : "indexed => [#{ind.join(", ")}]") |
| 290 | - end |
| 291 | - end |
| 292 | - end |
| 151 | + attrs = get_attributes(col, col_type, klass, options) |
| 293 152 | col_name = if with_comments?(klass, options) && col.comment |
| 294 | - "#{col.name}(#{col.comment})" |
| 153 | + "#{col.name}(#{col.comment.gsub(/\n/, "\\n")})" |
| 295 154 | else |
| 296 155 | col.name |
| 297 156 | end |
| 157 | + |
| 298 158 | if options[:format_rdoc] |
| 299 159 | info << sprintf("# %-#{max_size}.#{max_size}s<tt>%s</tt>", "*#{col_name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n" |
| 300 160 | elsif options[:format_yard] |
| @@ -739,14 +599,17 @@ module AnnotateModels | |
| 739 599 | |
| 740 600 | # Retrieve loaded model class |
| 741 601 | def get_loaded_model(model_path, file) |
| 742 | - |
| 743 | - |
| 602 | + unless skip_subdirectory_model_load |
| 603 | + loaded_model_class = get_loaded_model_by_path(model_path) |
| 604 | + return loaded_model_class if loaded_model_class |
| 605 | + end |
| 744 606 | |
| 745 607 | # We cannot get loaded model when `model_path` is loaded by Rails |
| 746 608 | # auto_load/eager_load paths. Try all possible model paths one by one. |
| 747 609 | absolute_file = File.expand_path(file) |
| 748 610 | model_paths = |
| 749 | - $LOAD_PATH. |
| 611 | + $LOAD_PATH.map(&:to_s) |
| 612 | + .select { |path| absolute_file.include?(path) } |
| 750 613 | .map { |path| absolute_file.sub(path, '').sub(/\.rb$/, '').sub(/^\//, '') } |
| 751 614 | model_paths |
| 752 615 | .map { |path| get_loaded_model_by_path(path) } |
| @@ -769,6 +632,7 @@ module AnnotateModels | |
| 769 632 | def parse_options(options = {}) |
| 770 633 | self.model_dir = split_model_dir(options[:model_dir]) if options[:model_dir] |
| 771 634 | self.root_dir = options[:root_dir] if options[:root_dir] |
| 635 | + self.skip_subdirectory_model_load = options[:skip_subdirectory_model_load].present? |
| 772 636 | end |
| 773 637 | |
| 774 638 | def split_model_dir(option_value) |
| @@ -809,7 +673,7 @@ module AnnotateModels | |
| 809 673 | begin |
| 810 674 | return false if /#{SKIP_ANNOTATION_PREFIX}.*/ =~ (File.exist?(file) ? File.read(file) : '') |
| 811 675 | klass = get_model_class(file) |
| 812 | - do_annotate = klass && |
| 676 | + do_annotate = klass.is_a?(Class) && |
| 813 677 | klass < ActiveRecord::Base && |
| 814 678 | (!options[:exclude_sti_subclasses] || !(klass.superclass < ActiveRecord::Base && klass.table_name == klass.superclass.table_name)) && |
| 815 679 | !klass.abstract_class? && |
| @@ -982,9 +846,7 @@ module AnnotateModels | |
| 982 846 | # Construct the foreign column name in the translations table |
| 983 847 | # eg. Model: Car, foreign column name: car_id |
| 984 848 | foreign_column_name = [ |
| 985 | - klass. |
| 986 | - .gsub('::Translation', '').gsub('::', '_') |
| 987 | - .downcase, |
| 849 | + klass.table_name.to_s.singularize, |
| 988 850 | '_id' |
| 989 851 | ].join.to_sym |
| 990 852 | |
| @@ -996,6 +858,55 @@ module AnnotateModels | |
| 996 858 | foreign_column_name |
| 997 859 | ] |
| 998 860 | end |
| 861 | + |
| 862 | + ## |
| 863 | + # Get the list of attributes that should be included in the annotation for |
| 864 | + # a given column. |
| 865 | + def get_attributes(column, column_type, klass, options) |
| 866 | + attrs = [] |
| 867 | + attrs << "default(#{schema_default(klass, column)})" unless column.default.nil? || hide_default?(column_type, options) |
| 868 | + attrs << 'unsigned' if column.respond_to?(:unsigned?) && column.unsigned? |
| 869 | + attrs << 'not null' unless column.null |
| 870 | + attrs << 'primary key' if klass.primary_key && (klass.primary_key.is_a?(Array) ? klass.primary_key.collect(&:to_sym).include?(column.name.to_sym) : column.name.to_sym == klass.primary_key.to_sym) |
| 871 | + |
| 872 | + if column_type == 'decimal' |
| 873 | + column_type << "(#{column.precision}, #{column.scale})" |
| 874 | + elsif !%w[spatial geometry geography].include?(column_type) |
| 875 | + if column.limit && !options[:format_yard] |
| 876 | + if column.limit.is_a? Array |
| 877 | + attrs << "(#{column.limit.join(', ')})" |
| 878 | + else |
| 879 | + column_type << "(#{column.limit})" unless hide_limit?(column_type, options) |
| 880 | + end |
| 881 | + end |
| 882 | + end |
| 883 | + |
| 884 | + # Check out if we got an array column |
| 885 | + attrs << 'is an Array' if column.respond_to?(:array) && column.array |
| 886 | + |
| 887 | + # Check out if we got a geometric column |
| 888 | + # and print the type and SRID |
| 889 | + if column.respond_to?(:geometry_type) |
| 890 | + attrs << [column.geometry_type, column.try(:srid)].compact.join(', ') |
| 891 | + elsif column.respond_to?(:geometric_type) && column.geometric_type.present? |
| 892 | + attrs << [column.geometric_type.to_s.downcase, column.try(:srid)].compact.join(', ') |
| 893 | + end |
| 894 | + |
| 895 | + # Check if the column has indices and print "indexed" if true |
| 896 | + # If the index includes another column, print it too. |
| 897 | + if options[:simple_indexes] && klass.table_exists?# Check out if this column is indexed |
| 898 | + indices = retrieve_indexes_from_table(klass) |
| 899 | + if indices = indices.select { |ind| ind.columns.include? column.name } |
| 900 | + indices.sort_by(&:name).each do |ind| |
| 901 | + next if ind.columns.is_a?(String) |
| 902 | + ind = ind.columns.reject! { |i| i == column.name } |
| 903 | + attrs << (ind.empty? ? "indexed" : "indexed => [#{ind.join(", ")}]") |
| 904 | + end |
| 905 | + end |
| 906 | + end |
| 907 | + |
| 908 | + attrs |
| 909 | + end |
| 999 910 | end |
| 1000 911 | |
| 1001 912 | class BadModelFileError < LoadError |
| @@ -0,0 +1,113 @@ | |
| 1 | + require_relative './helpers' |
| 2 | + |
| 3 | + module AnnotateRoutes |
| 4 | + class HeaderGenerator |
| 5 | + PREFIX = '== Route Map'.freeze |
| 6 | + PREFIX_MD = '## Route Map'.freeze |
| 7 | + HEADER_ROW = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action'].freeze |
| 8 | + |
| 9 | + class << self |
| 10 | + def generate(options = {}) |
| 11 | + new(options, routes_map(options)).generate |
| 12 | + end |
| 13 | + |
| 14 | + private :new |
| 15 | + |
| 16 | + private |
| 17 | + |
| 18 | + def routes_map(options) |
| 19 | + result = `rake routes`.chomp("\n").split(/\n/, -1) |
| 20 | + |
| 21 | + # In old versions of Rake, the first line of output was the cwd. Not so |
| 22 | + # much in newer ones. We ditch that line if it exists, and if not, we |
| 23 | + # keep the line around. |
| 24 | + result.shift if result.first =~ %r{^\(in \/} |
| 25 | + |
| 26 | + ignore_routes = options[:ignore_routes] |
| 27 | + regexp_for_ignoring_routes = ignore_routes ? /#{ignore_routes}/ : nil |
| 28 | + |
| 29 | + # Skip routes which match given regex |
| 30 | + # Note: it matches the complete line (route_name, path, controller/action) |
| 31 | + if regexp_for_ignoring_routes |
| 32 | + result.reject { |line| line =~ regexp_for_ignoring_routes } |
| 33 | + else |
| 34 | + result |
| 35 | + end |
| 36 | + end |
| 37 | + end |
| 38 | + |
| 39 | + def initialize(options, routes_map) |
| 40 | + @options = options |
| 41 | + @routes_map = routes_map |
| 42 | + end |
| 43 | + |
| 44 | + def generate |
| 45 | + magic_comments_map, contents_without_magic_comments = Helpers.extract_magic_comments_from_array(routes_map) |
| 46 | + |
| 47 | + out = [] |
| 48 | + |
| 49 | + magic_comments_map.each do |magic_comment| |
| 50 | + out << magic_comment |
| 51 | + end |
| 52 | + out << '' if magic_comments_map.any? |
| 53 | + |
| 54 | + out << comment(options[:wrapper_open]) if options[:wrapper_open] |
| 55 | + |
| 56 | + out << comment(markdown? ? PREFIX_MD : PREFIX) + timestamp_if_required |
| 57 | + out << comment |
| 58 | + return out if contents_without_magic_comments.size.zero? |
| 59 | + |
| 60 | + maxs = [HEADER_ROW.map(&:size)] + contents_without_magic_comments[1..-1].map { |line| line.split.map(&:size) } |
| 61 | + |
| 62 | + if markdown? |
| 63 | + max = maxs.map(&:max).compact.max |
| 64 | + |
| 65 | + out << comment(content(HEADER_ROW, maxs)) |
| 66 | + out << comment(content(['-' * max, '-' * max, '-' * max, '-' * max], maxs)) |
| 67 | + else |
| 68 | + out << comment(content(contents_without_magic_comments[0], maxs)) |
| 69 | + end |
| 70 | + |
| 71 | + out += contents_without_magic_comments[1..-1].map { |line| comment(content(markdown? ? line.split(' ') : line, maxs)) } |
| 72 | + out << comment(options[:wrapper_close]) if options[:wrapper_close] |
| 73 | + |
| 74 | + out |
| 75 | + end |
| 76 | + |
| 77 | + private |
| 78 | + |
| 79 | + attr_reader :options, :routes_map |
| 80 | + |
| 81 | + def comment(row = '') |
| 82 | + if row == '' |
| 83 | + '#' |
| 84 | + else |
| 85 | + "# #{row}" |
| 86 | + end |
| 87 | + end |
| 88 | + |
| 89 | + def content(line, maxs) |
| 90 | + return line.rstrip unless markdown? |
| 91 | + |
| 92 | + line.each_with_index.map { |elem, index| format_line_element(elem, maxs, index) }.join(' | ') |
| 93 | + end |
| 94 | + |
| 95 | + def format_line_element(elem, maxs, index) |
| 96 | + min_length = maxs.map { |arr| arr[index] }.max || 0 |
| 97 | + format("%-#{min_length}.#{min_length}s", elem.tr('|', '-')) |
| 98 | + end |
| 99 | + |
| 100 | + def markdown? |
| 101 | + options[:format_markdown] |
| 102 | + end |
| 103 | + |
| 104 | + def timestamp_if_required(time = Time.now) |
| 105 | + if options[:timestamp] |
| 106 | + time_formatted = time.strftime('%Y-%m-%d %H:%M') |
| 107 | + " (Updated #{time_formatted})" |
| 108 | + else |
| 109 | + '' |
| 110 | + end |
| 111 | + end |
| 112 | + end |
| 113 | + end |
| @@ -1,5 +1,3 @@ | |
| 1 | - # rubocop:disable Metrics/ModuleLength |
| 2 | - |
| 3 1 | # == Annotate Routes |
| 4 2 | # |
| 5 3 | # Based on: |
| @@ -21,18 +19,15 @@ | |
| 21 19 | # |
| 22 20 | |
| 23 21 | require_relative './annotate_routes/helpers' |
| 22 | + require_relative './annotate_routes/header_generator' |
| 24 23 | |
| 25 24 | module AnnotateRoutes |
| 26 | - PREFIX = '== Route Map'.freeze |
| 27 | - PREFIX_MD = '## Route Map'.freeze |
| 28 | - HEADER_ROW = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action'].freeze |
| 29 | - |
| 30 25 | class << self |
| 31 26 | def do_annotations(options = {}) |
| 32 27 | if routes_file_exist? |
| 33 28 | existing_text = File.read(routes_file) |
| 34 29 | content, header_position = Helpers.strip_annotations(existing_text) |
| 35 | - new_content = annotate_routes( |
| 30 | + new_content = annotate_routes(HeaderGenerator.generate(options), content, header_position, options) |
| 36 31 | new_text = new_content.join("\n") |
| 37 32 | |
| 38 33 | if rewrite_contents(existing_text, new_text) |
| @@ -71,49 +66,6 @@ module AnnotateRoutes | |
| 71 66 | @routes_rb ||= File.join('config', 'routes.rb') |
| 72 67 | end |
| 73 68 | |
| 74 | - def header(options = {}) |
| 75 | - routes_map = app_routes_map(options) |
| 76 | - |
| 77 | - magic_comments_map, routes_map = Helpers.extract_magic_comments_from_array(routes_map) |
| 78 | - |
| 79 | - out = [] |
| 80 | - |
| 81 | - magic_comments_map.each do |magic_comment| |
| 82 | - out << magic_comment |
| 83 | - end |
| 84 | - out << '' if magic_comments_map.any? |
| 85 | - |
| 86 | - out << comment(options[:wrapper_open]) if options[:wrapper_open] |
| 87 | - |
| 88 | - out << comment(options[:format_markdown] ? PREFIX_MD : PREFIX) + (options[:timestamp] ? " (Updated #{Time.now.strftime('%Y-%m-%d %H:%M')})" : '') |
| 89 | - out << comment |
| 90 | - return out if routes_map.size.zero? |
| 91 | - |
| 92 | - maxs = [HEADER_ROW.map(&:size)] + routes_map[1..-1].map { |line| line.split.map(&:size) } |
| 93 | - |
| 94 | - if options[:format_markdown] |
| 95 | - max = maxs.map(&:max).compact.max |
| 96 | - |
| 97 | - out << comment(content(HEADER_ROW, maxs, options)) |
| 98 | - out << comment(content(['-' * max, '-' * max, '-' * max, '-' * max], maxs, options)) |
| 99 | - else |
| 100 | - out << comment(content(routes_map[0], maxs, options)) |
| 101 | - end |
| 102 | - |
| 103 | - out += routes_map[1..-1].map { |line| comment(content(options[:format_markdown] ? line.split(' ') : line, maxs, options)) } |
| 104 | - out << comment(options[:wrapper_close]) if options[:wrapper_close] |
| 105 | - |
| 106 | - out |
| 107 | - end |
| 108 | - |
| 109 | - def comment(row = '') |
| 110 | - if row == '' |
| 111 | - '#' |
| 112 | - else |
| 113 | - "# #{row}" |
| 114 | - end |
| 115 | - end |
| 116 | - |
| 117 69 | def strip_on_removal(content, header_position) |
| 118 70 | if header_position == :before |
| 119 71 | content.shift while content.first == '' |
| @@ -162,32 +114,5 @@ module AnnotateRoutes | |
| 162 114 | |
| 163 115 | new_content |
| 164 116 | end |
| 165 | - |
| 166 | - def app_routes_map(options) |
| 167 | - routes_map = `rake routes`.chomp("\n").split(/\n/, -1) |
| 168 | - |
| 169 | - # In old versions of Rake, the first line of output was the cwd. Not so |
| 170 | - # much in newer ones. We ditch that line if it exists, and if not, we |
| 171 | - # keep the line around. |
| 172 | - routes_map.shift if routes_map.first =~ /^\(in \// |
| 173 | - |
| 174 | - # Skip routes which match given regex |
| 175 | - # Note: it matches the complete line (route_name, path, controller/action) |
| 176 | - if options[:ignore_routes] |
| 177 | - routes_map.reject! { |line| line =~ /#{options[:ignore_routes]}/ } |
| 178 | - end |
| 179 | - |
| 180 | - routes_map |
| 181 | - end |
| 182 | - |
| 183 | - def content(line, maxs, options = {}) |
| 184 | - return line.rstrip unless options[:format_markdown] |
| 185 | - |
| 186 | - line.each_with_index.map do |elem, index| |
| 187 | - min_length = maxs.map { |arr| arr[index] }.max || 0 |
| 188 | - |
| 189 | - sprintf("%-#{min_length}.#{min_length}s", elem.tr('|', '-')) |
| 190 | - end.join(' | ') |
| 191 | - end |
| 192 117 | end |
| 193 118 | end |
data/lib/annotate/version.rb CHANGED
| @@ -4,7 +4,20 @@ | |
| 4 4 | # Append annotations to Rake tasks for ActiveRecord, so annotate automatically gets |
| 5 5 | # run after doing db:migrate. |
| 6 6 | |
| 7 | - %w(db:migrate db:migrate:up db:migrate:down db:migrate:reset db:migrate:redo db:rollback) |
| 7 | + migration_tasks = %w(db:migrate db:migrate:up db:migrate:down db:migrate:reset db:migrate:redo db:rollback) |
| 8 | + if defined?(Rails::Application) && Rails.version.split('.').first.to_i >= 6 |
| 9 | + require 'active_record' |
| 10 | + |
| 11 | + databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml |
| 12 | + |
| 13 | + ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name| |
| 14 | + migration_tasks.concat(%w(db:migrate db:migrate:up db:migrate:down).map { |task| "#{task}:#{spec_name}" }) |
| 15 | + end |
| 16 | + end |
| 17 | + |
| 18 | + migration_tasks.each do |task| |
| 19 | + next unless Rake::Task.task_defined?(task) |
| 20 | + |
| 8 21 | Rake::Task[task].enhance do |
| 9 22 | Rake::Task[Rake.application.top_level_tasks.last].enhance do |
| 10 23 | annotation_options_task = if Rake::Task.task_defined?('app:set_annotation_options') |
| @@ -1,6 +1,12 @@ | |
| 1 | + annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__))) |
| 2 | + |
| 3 | + unless ENV['is_cli'] |
| 4 | + task :set_annotation_options |
| 5 | + task annotate_routes: :set_annotation_options |
| 6 | + end |
| 7 | + |
| 1 8 | desc "Adds the route map to routes.rb" |
| 2 9 | task :annotate_routes => :environment do |
| 3 | - annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__))) |
| 4 10 | require "#{annotate_lib}/annotate/annotate_routes" |
| 5 11 | |
| 6 12 | options={} |
metadata CHANGED
| @@ -1,7 +1,7 @@ | |
| 1 1 | --- !ruby/object:Gem::Specification |
| 2 2 | name: annotate |
| 3 3 | version: !ruby/object:Gem::Version |
| 4 | - version: 3. |
| 4 | + version: 3.2.0 |
| 5 5 | platform: ruby |
| 6 6 | authors: |
| 7 7 | - Alex Chaffee |
| @@ -12,7 +12,7 @@ authors: | |
| 12 12 | autorequire: |
| 13 13 | bindir: bin |
| 14 14 | cert_chain: [] |
| 15 | - date: |
| 15 | + date: 2022-02-10 00:00:00.000000000 Z |
| 16 16 | dependencies: |
| 17 17 | - !ruby/object:Gem::Dependency |
| 18 18 | name: rake |
| @@ -43,7 +43,7 @@ dependencies: | |
| 43 43 | version: '3.2' |
| 44 44 | - - "<" |
| 45 45 | - !ruby/object:Gem::Version |
| 46 | - version: ' |
| 46 | + version: '8.0' |
| 47 47 | type: :runtime |
| 48 48 | prerelease: false |
| 49 49 | version_requirements: !ruby/object:Gem::Requirement |
| @@ -53,7 +53,7 @@ dependencies: | |
| 53 53 | version: '3.2' |
| 54 54 | - - "<" |
| 55 55 | - !ruby/object:Gem::Version |
| 56 | - version: ' |
| 56 | + version: '8.0' |
| 57 57 | description: Annotates Rails/ActiveRecord Models, routes, fixtures, and others based |
| 58 58 | on the database schema. |
| 59 59 | email: |
| @@ -79,7 +79,9 @@ files: | |
| 79 79 | - lib/annotate.rb |
| 80 80 | - lib/annotate/active_record_patch.rb |
| 81 81 | - lib/annotate/annotate_models.rb |
| 82 | + - lib/annotate/annotate_models/file_patterns.rb |
| 82 83 | - lib/annotate/annotate_routes.rb |
| 84 | + - lib/annotate/annotate_routes/header_generator.rb |
| 83 85 | - lib/annotate/annotate_routes/helpers.rb |
| 84 86 | - lib/annotate/constants.rb |
| 85 87 | - lib/annotate/helpers.rb |
| @@ -93,11 +95,12 @@ files: | |
| 93 95 | - lib/tasks/annotate_models_migrate.rake |
| 94 96 | - lib/tasks/annotate_routes.rake |
| 95 97 | - potato.md |
| 96 | - homepage: |
| 98 | + homepage: https://github.com/ctran/annotate_models |
| 97 99 | licenses: |
| 98 100 | - Ruby |
| 99 101 | metadata: |
| 100 | - |
| 102 | + bug_tracker_uri: https://github.com/ctran/annotate_models/issues/ |
| 103 | + source_code_uri: https://github.com/ctran/annotate_models.git |
| 101 104 | post_install_message: |
| 102 105 | rdoc_options: [] |
| 103 106 | require_paths: |
| @@ -113,8 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 113 116 | - !ruby/object:Gem::Version |
| 114 117 | version: '0' |
| 115 118 | requirements: [] |
| 116 | - |
| 117 | - rubygems_version: 2.7.7 |
| 119 | + rubygems_version: 3.3.7 |
| 118 120 | signing_key: |
| 119 121 | specification_version: 4 |
| 120 122 | summary: Annotates Rails Models, routes, fixtures, and others based on the database |