rails-annotate-solargraph 0.1.1 → 0.2.2

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: 7d62415c07dd7235a4151593d2def72a55a7f8b7f7785f71eb66fe6996ce657d
4
+ data.tar.gz: a4737b994cb8ece0516da3d2e57ba66e05a5243cfc29ad1149f99bb03e1f8d1b
5
5
  SHA512:
6
- metadata.gz: 30d537b5e78042ba0dc990af3030b1ff1fb85232c3f998da008d1eaba6c83b2ab543c2c33adbe87ce181f4f6f9954ac500e077b4134dac9801498455e83e3b0e
7
- data.tar.gz: a36382741d8e9964835a8818a3091d26c4d18c3e798ec0e77636e47e1c7cd172f0f17086bc4869022b1552bfac91cded203def21723e9a99f3d687ab8fcba903
6
+ metadata.gz: b67bd482cf97936d6b55a9c28b5845a45a53650683cc05a89823427573aee2d66ac9500147b53c8b18ab74c6883f3f3306db864341aa03344ca5c376a6173892
7
+ data.tar.gz: 96d4d8aff31ee3a530635959539483572c268164c0214701e7b286cbcde37069419752ea3fd5b0bf466d5480dcdb4637f97952f764b932ea2824b8a14ec025c5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.2] - 2022-04-16
4
+
5
+ - Inexistent class loading error has been resolved
6
+
7
+ ## [0.2.1] - 2022-04-16
8
+
9
+ - Rakefile template refactoring and bug fix
10
+
11
+ ## [0.2.0] - 2022-04-16
12
+
13
+ - Associations get fully documented
14
+
15
+ ## [0.1.1] - 2022-04-15
16
+
17
+ - Minor bug fix after the initial release
18
+
3
19
  ## [0.1.0] - 2022-04-15
4
20
 
5
21
  - Initial release
22
+ - Automatic generation of annotations after migrations
23
+ - 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.2)
5
5
  rails (>= 5.0, < 8.0)
6
6
 
7
7
  GEM
@@ -5,12 +5,12 @@ if ::Rails.env.development?
5
5
  namespace :solargraph do
6
6
  desc "Add YARD comments documenting the models' schemas"
7
7
  task generate: :environment do
8
- system 'rails runner "p ::Rails::Annotate::Solargraph.generate"'
8
+ ::Rails::Annotate::Solargraph.generate
9
9
  end
10
10
 
11
11
  desc "Remove YARD comments documenting the models' schemas"
12
12
  task remove: :environment do
13
- system 'rails runner "p ::Rails::Annotate::Solargraph.remove"'
13
+ ::Rails::Annotate::Solargraph.remove
14
14
  end
15
15
  end
16
16
 
@@ -4,11 +4,29 @@ 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
 
18
+ class << (FAKE_MODEL_CLASS = Object.new)
19
+ def to_s
20
+ ::Object.to_s
21
+ end
22
+
23
+ alias name to_s
24
+
25
+ def table_name
26
+ '<unknown>'
27
+ end
28
+ end
29
+
12
30
  class << self
13
31
  # @param type [Symbol, String, nil]
14
32
  # @return [String]
@@ -51,7 +69,7 @@ module Rails
51
69
  # @param :write [Boolean]
52
70
  # @return [String] New file content.
53
71
  def annotate(write: true)
54
- file_content = remove_annotation write: false
72
+ old_content, file_content = remove_annotation write: false
55
73
 
56
74
  if CONFIG.annotation_position == :top
57
75
  magic_comments = file_content.scan(MAGIC_COMMENT_REGEXP).flatten.compact.join
@@ -63,35 +81,43 @@ module Rails
63
81
  end
64
82
 
65
83
  return new_file_content unless write
66
- return new_file_content if file_content == new_file_content
84
+ # debugger
85
+ return new_file_content if old_content == new_file_content
67
86
 
68
- ::File.write @file_name, new_file_content
87
+ write_file @file_name, new_file_content
69
88
  new_file_content
70
89
  end
71
90
 
72
91
  # @param :write [Boolean]
73
- # @return [String] New file content.
92
+ # @return [Array<String>] Old file content followed by new content.
74
93
  def remove_annotation(write: true)
75
94
  file_content = ::File.read(@file_name)
76
95
  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
96
+ result = [file_content, new_file_content]
97
+ return result unless write
98
+ return result if file_content == new_file_content
79
99
 
80
- ::File.write @file_name, new_file_content
81
- new_file_content
100
+ write_file @file_name, new_file_content
101
+ result
82
102
  end
83
103
 
84
104
  # @return [String]
85
105
  def annotation
86
- result = ::String.new
87
- result << <<~DOC
106
+ doc_string = ::String.new
107
+ doc_string << <<~DOC
88
108
  #{ANNOTATION_START}
89
109
  # @!parse
90
110
  # class #{@klass} < #{@klass.superclass}
91
111
  DOC
92
112
 
113
+ @klass.reflections.sort.each do |attr_name, reflection|
114
+ next document_polymorphic_relation(doc_string, attr_name, reflection) if reflection.polymorphic?
115
+
116
+ document_relation(doc_string, attr_name, reflection)
117
+ end
118
+
93
119
  @klass.attribute_types.each do |name, attr_type|
94
- result << <<~DOC
120
+ doc_string << <<~DOC
95
121
  # # Database column `#{@klass.table_name}.#{name}`, type: `#{attr_type.type}`.
96
122
  # # @param val [#{yard_type attr_type}, nil]
97
123
  # def #{name}=(val); end
@@ -101,7 +127,7 @@ module Rails
101
127
  DOC
102
128
  end
103
129
 
104
- result << <<~DOC.chomp
130
+ doc_string << <<~DOC.chomp
105
131
  # end
106
132
  # #{ANNOTATION_END}
107
133
  DOC
@@ -109,6 +135,79 @@ module Rails
109
135
 
110
136
  private
111
137
 
138
+ # @param file_name [String]
139
+ # @return [String]
140
+ def relative_file_name(file_name)
141
+ file_name.delete_prefix("#{::Rails.root}/")
142
+ end
143
+
144
+ # @param file_name [String]
145
+ # @param content [String]
146
+ # @return [void]
147
+ def write_file(file_name, content)
148
+ ::File.write(file_name, content)
149
+ puts "modify".rjust(12).with_styles(:bold, :green) + " #{relative_file_name(file_name)}"
150
+ end
151
+
152
+ # @return [String]
153
+ def klass_relation_name
154
+ @klass.table_name[..-2]
155
+ end
156
+
157
+ # @param doc_string [String]
158
+ # @param attr_name [String]
159
+ # @param reflection [ActiveRecord::Reflection::AbstractReflection]
160
+ # @return [void]
161
+ def document_relation(doc_string, attr_name, reflection)
162
+ reflection_class = begin
163
+ reflection.klass
164
+ rescue ::NameError
165
+ FAKE_MODEL_CLASS
166
+ end
167
+
168
+ db_description = \
169
+ case reflection
170
+ when ::ActiveRecord::Reflection::BelongsToReflection
171
+ type_docstring = reflection_class
172
+ "`belongs_to` relation with `#{reflection_class}`. Database column `#{@klass.table_name}.#{reflection.foreign_key}`."
173
+ when ::ActiveRecord::Reflection::HasOneReflection
174
+ type_docstring = reflection_class
175
+ "`has_one` relation with `#{reflection_class}`. Database column `#{reflection_class.table_name}.#{reflection.foreign_key}`."
176
+ when ::ActiveRecord::Reflection::HasManyReflection
177
+ type_docstring = "Array<#{reflection_class}>"
178
+ "`has_many` relation with `#{reflection_class}`. Database column `#{reflection_class.table_name}.#{reflection.foreign_key}`."
179
+ end
180
+
181
+ doc_string << <<~DOC
182
+ # # #{db_description}
183
+ # # @param val [#{type_docstring}, nil]
184
+ # def #{attr_name}=(val); end
185
+ # # #{db_description}
186
+ # # @return [#{type_docstring}, nil]
187
+ # def #{attr_name}; end
188
+ DOC
189
+ end
190
+
191
+ # @param doc_string [String]
192
+ # @param attr_name [String]
193
+ # @param reflection [ActiveRecord::Reflection::AbstractReflection]
194
+ # @return [void]
195
+ def document_polymorphic_relation(doc_string, attr_name, reflection)
196
+ classes = Solargraph.model_classes.select do |model_class|
197
+ model_class.reflections[klass_relation_name]&.options&.[](:as)&.to_sym == attr_name.to_sym
198
+ end
199
+
200
+ classes_string = classes.join(', ')
201
+ doc_string << <<~DOC
202
+ # # Polymorphic relation. Database columns `#{@klass.table_name}.#{attr_name}_id` and `#{@klass.table_name}.#{attr_name}_type`.
203
+ # # @param val [#{classes_string}, nil]
204
+ # def #{attr_name}=(val); end
205
+ # # Polymorphic relation. Database columns `#{@klass.table_name}.#{attr_name}_id` and `#{@klass.table_name}.#{attr_name}_type`.
206
+ # # @return [#{classes_string}, nil]
207
+ # def #{attr_name}; end
208
+ DOC
209
+ end
210
+
112
211
  # @param attr_type [ActiveModel::Type::Value]
113
212
  # @return [String]
114
213
  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.2'
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.2
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