rails-annotate-solargraph 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7bb66133bae3d570210fe56d3b0b707666551af81bbc2923a8cf7d589ec0ad8
4
- data.tar.gz: 4591722333e2d9b770ba3b33887a85ed37e846d7a8f3a03913cba4ab4627f828
3
+ metadata.gz: 744b5deaf3bfdd5fdf4b81b54abd8f4672336402bce178af67b04ff704b6ceab
4
+ data.tar.gz: 062d49842f237ddb38202975c0c88733fb3a825ebcb4c7cbd7641b45b58ee993
5
5
  SHA512:
6
- metadata.gz: 30d537b5e78042ba0dc990af3030b1ff1fb85232c3f998da008d1eaba6c83b2ab543c2c33adbe87ce181f4f6f9954ac500e077b4134dac9801498455e83e3b0e
7
- data.tar.gz: a36382741d8e9964835a8818a3091d26c4d18c3e798ec0e77636e47e1c7cd172f0f17086bc4869022b1552bfac91cded203def21723e9a99f3d687ab8fcba903
6
+ metadata.gz: 35eb981e6a0b52331164d80fd7238aadae0403cd9f4d93c7dc9a6c00e31714f6b731c23e961542ee5eac64a48c2820de926fcacaea1e6b8ff9b34c501781c05b
7
+ data.tar.gz: 6d0f472969a50ed833398216789d0000b8c288685946ab90621f48edca85f57034350df39a5006ae5ddb6b4a30623658c529584e6eca504b0202ff6b633b3379
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2022-04-16
4
+
5
+ - Associations get fully documented
6
+
7
+ ## [0.1.1] - 2022-04-15
8
+
9
+ - Minor bug fix after the initial release
10
+
3
11
  ## [0.1.0] - 2022-04-15
4
12
 
5
13
  - Initial release
14
+ - Automatic generation of annotations after migrations
15
+ - Manual rake tasks `annotate:solargraph:generate`, `annotate:solargraph:remove`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rails-annotate-solargraph (0.1.1)
4
+ rails-annotate-solargraph (0.2.0)
5
5
  rails (>= 5.0, < 8.0)
6
6
 
7
7
  GEM
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ include ::Rails::Annotate::Solargraph::TerminalColors
4
+
3
5
  if ::Rails.env.development?
4
6
  namespace :annotate do
5
7
  namespace :solargraph do
6
8
  desc "Add YARD comments documenting the models' schemas"
7
9
  task generate: :environment do
8
- system 'rails runner "p ::Rails::Annotate::Solargraph.generate"'
10
+ system! 'bundle exec bin/rails runner "::Rails::Annotate::Solargraph.generate"'
9
11
  end
10
12
 
11
13
  desc "Remove YARD comments documenting the models' schemas"
12
14
  task remove: :environment do
13
- system 'rails runner "p ::Rails::Annotate::Solargraph.remove"'
15
+ system! 'bundle exec bin/rails runner "::Rails::Annotate::Solargraph.remove"'
14
16
  end
15
17
  end
16
18
 
@@ -23,4 +25,8 @@ if ::Rails.env.development?
23
25
  end
24
26
  end
25
27
  end
28
+
29
+ def system!(val)
30
+ system(val) || abort(error_string("Command `#{val}` failed"))
31
+ end
26
32
  end
@@ -4,9 +4,15 @@ module Rails
4
4
  module Annotate
5
5
  module Solargraph
6
6
  class Model
7
+ using TerminalColors::Refinement
8
+
9
+ # @return [String]
7
10
  ANNOTATION_START = "\n# %%<RailsAnnotateSolargraph:Start>%%"
11
+ # @return [String]
8
12
  ANNOTATION_END = "%%<RailsAnnotateSolargraph:End>%%\n\n"
13
+ # @return [Regexp]
9
14
  ANNOTATION_REGEXP = /#{ANNOTATION_START}.*#{ANNOTATION_END}/m.freeze
15
+ # @return [Regexp]
10
16
  MAGIC_COMMENT_REGEXP = /(^#\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
17
 
12
18
  class << self
@@ -51,7 +57,7 @@ module Rails
51
57
  # @param :write [Boolean]
52
58
  # @return [String] New file content.
53
59
  def annotate(write: true)
54
- file_content = remove_annotation write: false
60
+ old_content, file_content = remove_annotation write: false
55
61
 
56
62
  if CONFIG.annotation_position == :top
57
63
  magic_comments = file_content.scan(MAGIC_COMMENT_REGEXP).flatten.compact.join
@@ -63,35 +69,43 @@ module Rails
63
69
  end
64
70
 
65
71
  return new_file_content unless write
66
- return new_file_content if file_content == new_file_content
72
+ # debugger
73
+ return new_file_content if old_content == new_file_content
67
74
 
68
- ::File.write @file_name, new_file_content
75
+ write_file @file_name, new_file_content
69
76
  new_file_content
70
77
  end
71
78
 
72
79
  # @param :write [Boolean]
73
- # @return [String] New file content.
80
+ # @return [Array<String>] Old file content followed by new content.
74
81
  def remove_annotation(write: true)
75
82
  file_content = ::File.read(@file_name)
76
83
  new_file_content = file_content.sub(ANNOTATION_REGEXP, '')
77
- return new_file_content unless write
78
- return new_file_content if file_content == new_file_content
84
+ result = [file_content, new_file_content]
85
+ return result unless write
86
+ return result if file_content == new_file_content
79
87
 
80
- ::File.write @file_name, new_file_content
81
- new_file_content
88
+ write_file @file_name, new_file_content
89
+ result
82
90
  end
83
91
 
84
92
  # @return [String]
85
93
  def annotation
86
- result = ::String.new
87
- result << <<~DOC
94
+ doc_string = ::String.new
95
+ doc_string << <<~DOC
88
96
  #{ANNOTATION_START}
89
97
  # @!parse
90
98
  # class #{@klass} < #{@klass.superclass}
91
99
  DOC
92
100
 
101
+ @klass.reflections.sort.each do |attr_name, reflection|
102
+ next document_polymorphic_relation(doc_string, attr_name, reflection) if reflection.polymorphic?
103
+
104
+ document_relation(doc_string, attr_name, reflection)
105
+ end
106
+
93
107
  @klass.attribute_types.each do |name, attr_type|
94
- result << <<~DOC
108
+ doc_string << <<~DOC
95
109
  # # Database column `#{@klass.table_name}.#{name}`, type: `#{attr_type.type}`.
96
110
  # # @param val [#{yard_type attr_type}, nil]
97
111
  # def #{name}=(val); end
@@ -101,7 +115,7 @@ module Rails
101
115
  DOC
102
116
  end
103
117
 
104
- result << <<~DOC.chomp
118
+ doc_string << <<~DOC.chomp
105
119
  # end
106
120
  # #{ANNOTATION_END}
107
121
  DOC
@@ -109,6 +123,73 @@ module Rails
109
123
 
110
124
  private
111
125
 
126
+ # @param file_name [String]
127
+ # @return [String]
128
+ def relative_file_name(file_name)
129
+ file_name.delete_prefix("#{::Rails.root}/")
130
+ end
131
+
132
+ # @param file_name [String]
133
+ # @param content [String]
134
+ # @return [void]
135
+ def write_file(file_name, content)
136
+ ::File.write(file_name, content)
137
+ puts "modify".rjust(12).with_styles(:bold, :green) + " #{relative_file_name(file_name)}"
138
+ end
139
+
140
+ # @return [String]
141
+ def klass_relation_name
142
+ @klass.table_name[..-2]
143
+ end
144
+
145
+ # @param doc_string [String]
146
+ # @param attr_name [String]
147
+ # @param reflection [ActiveRecord::Reflection::AbstractReflection]
148
+ # @return [void]
149
+ def document_relation(doc_string, attr_name, reflection)
150
+ db_description = \
151
+ case reflection
152
+ when ::ActiveRecord::Reflection::BelongsToReflection
153
+ type_docstring = reflection.klass
154
+ "`belongs_to` relation with `#{reflection.klass}`. Database column `#{@klass.table_name}.#{reflection.foreign_key}`."
155
+ when ::ActiveRecord::Reflection::HasOneReflection
156
+ type_docstring = reflection.klass
157
+ "`has_one` relation with `#{reflection.klass}`. Database column `#{reflection.klass.table_name}.#{reflection.foreign_key}`."
158
+ when ::ActiveRecord::Reflection::HasManyReflection
159
+ type_docstring = "Array<#{reflection.klass}>"
160
+ "`has_many` relation with `#{reflection.klass}`. Database column `#{reflection.klass.table_name}.#{reflection.foreign_key}`."
161
+ end
162
+
163
+ doc_string << <<~DOC
164
+ # # #{db_description}
165
+ # # @param val [#{type_docstring}, nil]
166
+ # def #{attr_name}=(val); end
167
+ # # #{db_description}
168
+ # # @return [#{type_docstring}, nil]
169
+ # def #{attr_name}; end
170
+ DOC
171
+ end
172
+
173
+ # @param doc_string [String]
174
+ # @param attr_name [String]
175
+ # @param reflection [ActiveRecord::Reflection::AbstractReflection]
176
+ # @return [void]
177
+ def document_polymorphic_relation(doc_string, attr_name, reflection)
178
+ classes = Solargraph.model_classes.select do |model_class|
179
+ model_class.reflections[klass_relation_name]&.options&.[](:as)&.to_sym == attr_name.to_sym
180
+ end
181
+
182
+ classes_string = classes.join(', ')
183
+ doc_string << <<~DOC
184
+ # # Polymorphic relation. Database columns `#{@klass.table_name}.#{attr_name}_id` and `#{@klass.table_name}.#{attr_name}_type`.
185
+ # # @param val [#{classes_string}, nil]
186
+ # def #{attr_name}=(val); end
187
+ # # Polymorphic relation. Database columns `#{@klass.table_name}.#{attr_name}_id` and `#{@klass.table_name}.#{attr_name}_type`.
188
+ # # @return [#{classes_string}, nil]
189
+ # def #{attr_name}; end
190
+ DOC
191
+ end
192
+
112
193
  # @param attr_type [ActiveModel::Type::Value]
113
194
  # @return [String]
114
195
  def yard_type(attr_type)
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Annotate
5
+ module Solargraph
6
+ module TerminalColors
7
+ extend self
8
+
9
+ # @return [Hash{Symbol => String}]
10
+ MAP = {
11
+ blue: (BLUE = "\033[94m"),
12
+ cyan: (CYAN = "\033[96m"),
13
+ green: (GREEN = "\033[92m"),
14
+ yellow: (YELLOW = "\033[93m"),
15
+ red: (RED = "\033[91m"),
16
+ terminate: (TERMINATE = "\033[0m"),
17
+ bold: (BOLD = "\033[1m"),
18
+ italic: (ITALIC = "\033[3m"),
19
+ underline: (UNDERLINE = "\033[4m")
20
+ }.freeze
21
+
22
+ class << self
23
+ # Style a string with an ASCII escape code
24
+ #
25
+ # @param string [String]
26
+ # @param style [Symbol]
27
+ # @return [String]
28
+ def with_style(string, style)
29
+ "#{MAP[style]}#{string}#{TERMINATE}"
30
+ end
31
+
32
+ # Style a string with multiple ASCII escape codes
33
+ #
34
+ # @param string [String]
35
+ # @param styles [Array<Symbol>]
36
+ # @return [String]
37
+ def with_styles(string, *styles)
38
+ result = ::String.new
39
+ styles.each do |style|
40
+ result << MAP[style]
41
+ end
42
+
43
+ result << "#{string}#{TERMINATE}"
44
+ end
45
+ end
46
+
47
+ def title_string(string)
48
+ TerminalColors.with_styles "== #{string} ==", :cyan, :underline, :italic
49
+ end
50
+
51
+ def error_string(string)
52
+ TerminalColors.with_styles "!! #{string} !!", :bold, :red
53
+ end
54
+
55
+ def title(string)
56
+ puts "\n", title_string(string)
57
+ end
58
+
59
+ def error(string)
60
+ puts "\n", error_string(string)
61
+ end
62
+
63
+ module Refinement
64
+ refine ::String do
65
+ # @param styles [Array<Symbol]>
66
+ # @return [self] Colored string
67
+ def with_styles(*styles)
68
+ ::Rails::Annotate::Solargraph::TerminalColors.with_styles(self, *styles)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -3,7 +3,7 @@
3
3
  module Rails
4
4
  module Annotate
5
5
  module Solargraph
6
- VERSION = '0.1.1'
6
+ VERSION = '0.2.0'
7
7
  end
8
8
  end
9
9
  end
@@ -4,6 +4,7 @@ require 'set'
4
4
 
5
5
  require_relative "solargraph/version"
6
6
  require_relative "solargraph/configuration"
7
+ require_relative "solargraph/terminal_colors"
7
8
  require_relative "solargraph/model"
8
9
 
9
10
  module Rails
@@ -16,15 +17,19 @@ module Rails
16
17
  RAKEFILE_NAME = 'rails_annotate_solargraph.rake'
17
18
  # @return [Configuration]
18
19
  CONFIG = Configuration.new
20
+ # @return [Set<Symbol>]
21
+ VALID_MODIFICATION_METHODS = ::Set[:annotate, :remove_annotation].freeze
19
22
 
20
23
  class << self
21
24
  # @return [Array<String>] Array of changed files.
22
25
  def generate
26
+ title 'Generating model schema annotations'
23
27
  modify_models :annotate
24
28
  end
25
29
 
26
30
  # @return [Array<String>] Array of changed files.
27
31
  def remove
32
+ title 'Removing model schema annotations'
28
33
  modify_models :remove_annotation
29
34
  end
30
35
 
@@ -35,10 +40,15 @@ module Rails
35
40
 
36
41
  alias call generate
37
42
 
38
- VALID_MODIFICATION_METHODS = ::Set[:annotate, :remove_annotation]
43
+ # @return [Array<ActiveRecord::Base>]
44
+ def model_classes
45
+ @model_classes ||= (::ApplicationRecord rescue ::ActiveRecord::Base).subclasses.sort_by(&:name)
46
+ end
39
47
 
40
48
  private
41
49
 
50
+ include TerminalColors
51
+
42
52
  # @param method [Symbol] Name of the method that will be called on every loaded Model
43
53
  # @return [Array<String>] Array of changed files.
44
54
  def modify_models(method)
@@ -49,7 +59,7 @@ module Rails
49
59
  model_files = ::Dir[::File.join(::Rails.root, MODEL_DIR, '**/*.rb')].map { |file| file.sub("#{::Rails.root}/", '') }.to_set
50
60
 
51
61
  ::Rails.application.eager_load!
52
- (::ApplicationRecord rescue ::ActiveRecord::Base).subclasses.each do |subclass|
62
+ model_classes.each do |subclass|
53
63
  subclass_file = ::File.join MODEL_DIR, "#{subclass.to_s.underscore}.rb"
54
64
  next unless model_files.include? subclass_file
55
65
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-annotate-solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Drewniak
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-15 00:00:00.000000000 Z
11
+ date: 2022-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -57,6 +57,7 @@ files:
57
57
  - lib/rails/annotate/solargraph.rb
58
58
  - lib/rails/annotate/solargraph/configuration.rb
59
59
  - lib/rails/annotate/solargraph/model.rb
60
+ - lib/rails/annotate/solargraph/terminal_colors.rb
60
61
  - lib/rails/annotate/solargraph/version.rb
61
62
  - rails-annotate-solargraph.gemspec
62
63
  - sig/rails/annotate/solargraph.rbs