From 06255470a5d28bb60d39e20fae9ab50430c27c96 Mon Sep 17 00:00:00 2001 From: tvallois Date: Fri, 24 Jan 2020 06:30:08 +0100 Subject: [PATCH] Support YARD notation (#724) The scope of this pull request is to allow annotate_models to generate models documentation using YARD. This is the first step, I'll add more features later. --- .rubocop_todo.yml | 8 ++++---- lib/annotate/annotate_models.rb | 19 +++++++++++++++++- lib/annotate/constants.rb | 2 +- lib/annotate/parser.rb | 4 ++-- .../templates/auto_annotate_models.rake | 1 + lib/tasks/annotate_models.rake | 1 + .../lib/tasks/auto_annotate_models.rake | 1 + .../lib/tasks/auto_annotate_models.rake | 1 + spec/lib/annotate/annotate_models_spec.rb | 20 +++++++++++++++++++ 9 files changed, 49 insertions(+), 8 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 0238dc4b4..5c0697b68 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -408,7 +408,7 @@ Lint/ShadowingOuterLocalVariable: # Offense count: 20 Metrics/AbcSize: - Max: 141 + Max: 155 # Offense count: 31 # Configuration parameters: CountComments, ExcludedMethods. @@ -423,16 +423,16 @@ Metrics/BlockNesting: # Offense count: 10 Metrics/CyclomaticComplexity: - Max: 36 + Max: 41 # Offense count: 30 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: - Max: 75 + Max: 80 # Offense count: 8 Metrics/PerceivedComplexity: - Max: 42 + Max: 47 # Offense count: 1 Naming/AccessorMethodName: diff --git a/lib/annotate/annotate_models.rb b/lib/annotate/annotate_models.rb index 004e473cf..d6e3a0866 100644 --- a/lib/annotate/annotate_models.rb +++ b/lib/annotate/annotate_models.rb @@ -258,7 +258,7 @@ def get_schema_info(klass, header, options = {}) if col_type == 'decimal' col_type << "(#{col.precision}, #{col.scale})" elsif !%w[spatial geometry geography].include?(col_type) - if col.limit + if col.limit && !options[:format_yard] if col.limit.is_a? Array attrs << "(#{col.limit.join(', ')})" else @@ -297,6 +297,10 @@ def get_schema_info(klass, header, options = {}) end if options[:format_rdoc] info << sprintf("# %-#{max_size}.#{max_size}s%s", "*#{col_name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n" + elsif options[:format_yard] + info << sprintf("# @!attribute #{col_name}") + "\n" + ruby_class = col.respond_to?(:array) && col.array ? "Array<#{map_col_type_to_ruby_classes(col_type)}>": map_col_type_to_ruby_classes(col_type) + info << sprintf("# @return [#{ruby_class}]") + "\n" elsif options[:format_markdown] name_remainder = max_size - col_name.length - non_ascii_length(col_name) type_remainder = (md_type_allowance - 2) - col_type.length @@ -931,6 +935,19 @@ def non_ascii_length(string) string.to_s.chars.reject(&:ascii_only?).length end + def map_col_type_to_ruby_classes(col_type) + case col_type + when 'integer' then Integer.to_s + when 'float' then Float.to_s + when 'decimal' then BigDecimal.to_s + when 'datetime', 'timestamp', 'time' then Time.to_s + when 'date' then Date.to_s + when 'text', 'string', 'binary', 'inet', 'uuid' then String.to_s + when 'json', 'jsonb' then Hash.to_s + when 'boolean' then 'Boolean' + end + end + def columns(klass, options) cols = klass.columns cols += translated_columns(klass) diff --git a/lib/annotate/constants.rb b/lib/annotate/constants.rb index bc7f6a01a..cd2148f39 100644 --- a/lib/annotate/constants.rb +++ b/lib/annotate/constants.rb @@ -14,7 +14,7 @@ module Constants FLAG_OPTIONS = [ :show_indexes, :simple_indexes, :include_version, :exclude_tests, :exclude_fixtures, :exclude_factories, :ignore_model_sub_dir, - :format_bare, :format_rdoc, :format_markdown, :sort, :force, :frozen, + :format_bare, :format_rdoc, :format_yard, :format_markdown, :sort, :force, :frozen, :trace, :timestamp, :exclude_serializers, :classified_sort, :show_foreign_keys, :show_complete_foreign_keys, :exclude_scaffolds, :exclude_controllers, :exclude_helpers, diff --git a/lib/annotate/parser.rb b/lib/annotate/parser.rb index 2b08702af..82d80161e 100644 --- a/lib/annotate/parser.rb +++ b/lib/annotate/parser.rb @@ -12,7 +12,7 @@ def self.parse(args, env = {}) ANNOTATION_POSITIONS = %w[before top after bottom].freeze FILE_TYPE_POSITIONS = %w[position_in_class position_in_factory position_in_fixture position_in_test position_in_routes position_in_serializer].freeze EXCLUSION_LIST = %w[tests fixtures factories serializers].freeze - FORMAT_TYPES = %w[bare rdoc markdown].freeze + FORMAT_TYPES = %w[bare rdoc yard markdown].freeze def initialize(args, env) @args = args @@ -196,7 +196,7 @@ def add_options_to_parser(option_parser) # rubocop:disable Metrics/MethodLength exclusions.each { |exclusion| env["exclude_#{exclusion}"] = 'yes' } end - option_parser.on('-f', '--format [bare|rdoc|markdown]', FORMAT_TYPES, 'Render Schema Infomation as plain/RDoc/Markdown') do |fmt| + option_parser.on('-f', '--format [bare|rdoc|yard|markdown]', FORMAT_TYPES, 'Render Schema Infomation as plain/RDoc/Yard/Markdown') do |fmt| env["format_#{fmt}"] = 'yes' end diff --git a/lib/generators/annotate/templates/auto_annotate_models.rake b/lib/generators/annotate/templates/auto_annotate_models.rake index 69e1e2a82..1f355249b 100644 --- a/lib/generators/annotate/templates/auto_annotate_models.rake +++ b/lib/generators/annotate/templates/auto_annotate_models.rake @@ -42,6 +42,7 @@ if Rails.env.development? 'skip_on_db_migrate' => 'false', 'format_bare' => 'true', 'format_rdoc' => 'false', + 'format_yard' => 'false', 'format_markdown' => 'false', 'sort' => 'false', 'force' => 'false', diff --git a/lib/tasks/annotate_models.rake b/lib/tasks/annotate_models.rake index dcb10892a..76d8cfe63 100644 --- a/lib/tasks/annotate_models.rake +++ b/lib/tasks/annotate_models.rake @@ -37,6 +37,7 @@ task annotate_models: :environment do options[:ignore_model_sub_dir] = Annotate::Helpers.true?(ENV['ignore_model_sub_dir']) options[:format_bare] = Annotate::Helpers.true?(ENV['format_bare']) options[:format_rdoc] = Annotate::Helpers.true?(ENV['format_rdoc']) + options[:format_yard] = Annotate::Helpers.true?(ENV['format_yard']) options[:format_markdown] = Annotate::Helpers.true?(ENV['format_markdown']) options[:sort] = Annotate::Helpers.true?(ENV['sort']) options[:force] = Annotate::Helpers.true?(ENV['force']) diff --git a/spec/integration/rails_4.1.1/lib/tasks/auto_annotate_models.rake b/spec/integration/rails_4.1.1/lib/tasks/auto_annotate_models.rake index 842eade72..d1983e31f 100644 --- a/spec/integration/rails_4.1.1/lib/tasks/auto_annotate_models.rake +++ b/spec/integration/rails_4.1.1/lib/tasks/auto_annotate_models.rake @@ -24,6 +24,7 @@ if Rails.env.development? 'skip_on_db_migrate' => "false", 'format_bare' => "true", 'format_rdoc' => "false", + 'format_yard' => "false", 'format_markdown' => "false", 'sort' => "false", 'force' => "false", diff --git a/spec/integration/rails_4.2.0/lib/tasks/auto_annotate_models.rake b/spec/integration/rails_4.2.0/lib/tasks/auto_annotate_models.rake index 842eade72..d1983e31f 100644 --- a/spec/integration/rails_4.2.0/lib/tasks/auto_annotate_models.rake +++ b/spec/integration/rails_4.2.0/lib/tasks/auto_annotate_models.rake @@ -24,6 +24,7 @@ if Rails.env.development? 'skip_on_db_migrate' => "false", 'format_bare' => "true", 'format_rdoc' => "false", + 'format_yard' => "false", 'format_markdown' => "false", 'sort' => "false", 'force' => "false", diff --git a/spec/lib/annotate/annotate_models_spec.rb b/spec/lib/annotate/annotate_models_spec.rb index a01de1aa0..748078aea 100644 --- a/spec/lib/annotate/annotate_models_spec.rb +++ b/spec/lib/annotate/annotate_models_spec.rb @@ -632,6 +632,26 @@ def mock_column(name, type, options = {}) EOS end + it 'should get schema info as YARD' do + klass = mock_class(:users, + :id, + [ + mock_column(:id, :integer), + mock_column(:name, :string, limit: 50), + ]) + expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_yard: true)).to eql(<<-EOS) +# #{AnnotateModels::PREFIX} +# +# Table name: users +# +# @!attribute id +# @return [Integer] +# @!attribute name +# @return [String] +# +EOS + end + it 'should get schema info as Markdown' do klass = mock_class(:users, :id,