rails-annotate-solargraph 0.2.2 → 0.4.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: 7d62415c07dd7235a4151593d2def72a55a7f8b7f7785f71eb66fe6996ce657d
4
- data.tar.gz: a4737b994cb8ece0516da3d2e57ba66e05a5243cfc29ad1149f99bb03e1f8d1b
3
+ metadata.gz: 80d2fcb8fc2090d6a1eabc8f510b3b9fec1608da1870c7d88b301ce64907920a
4
+ data.tar.gz: 9553090d359f8f829e955349cac6c527c43335433e38d91152862780cce3ab63
5
5
  SHA512:
6
- metadata.gz: b67bd482cf97936d6b55a9c28b5845a45a53650683cc05a89823427573aee2d66ac9500147b53c8b18ab74c6883f3f3306db864341aa03344ca5c376a6173892
7
- data.tar.gz: 96d4d8aff31ee3a530635959539483572c268164c0214701e7b286cbcde37069419752ea3fd5b0bf466d5480dcdb4637f97952f764b932ea2824b8a14ec025c5
6
+ metadata.gz: 8b2415d929d939454a4f65277d85baa03935219226c6ad5b35c35264cd2de94bb1b1d55a55a55ee8cc737523210e3653c648bea50798dd53b580767fbb6a2eec
7
+ data.tar.gz: 9436e870a6cbc28e636151b76717cde5bf4186f44bc8af4293ada785f1c50e039895fcf5cb38dd27868c5e88027df9f52be8ee070591e0635de30fdba7437aef
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "cSpell.words": [
3
3
  "activerecord",
4
+ "klass",
4
5
  "rakefile",
5
6
  "solargraph",
6
7
  "yieldparam"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2022-04-17
4
+
5
+ - Annotations get saved to a schema file by default `app/models/annotate_solargraph_schema.rb`
6
+
7
+ ## [0.3.0] - 2022-04-17
8
+
9
+ - `has_many :through` and `has_one :through` relations get documented
10
+
11
+ ## [0.2.3] - 2022-04-16
12
+
13
+ - A nicer fix for the previous problem
14
+
3
15
  ## [0.2.2] - 2022-04-16
4
16
 
5
17
  - Inexistent class loading error has been resolved
@@ -11,6 +23,7 @@
11
23
  ## [0.2.0] - 2022-04-16
12
24
 
13
25
  - Associations get fully documented
26
+ - `has_many`, `has_one` and `belongs_to` relations get documented
14
27
 
15
28
  ## [0.1.1] - 2022-04-15
16
29
 
@@ -20,4 +33,5 @@
20
33
 
21
34
  - Initial release
22
35
  - Automatic generation of annotations after migrations
36
+ - Database fields get documented
23
37
  - 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.2.2)
4
+ rails-annotate-solargraph (0.4.0)
5
5
  rails (>= 5.0, < 8.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -63,11 +63,31 @@ You can change the gem's default configuration like so:
63
63
  ```ruby
64
64
  # config/initializers/rails_annotate_solargraph.rb
65
65
 
66
- Rails::Annotate::Solargraph.configure do |conf|
67
- conf.annotation_position = :top # `:bottom` by default
66
+ if ::Rails.env.development?
67
+ ::Rails::Annotate::Solargraph.configure do |conf|
68
+ conf.annotation_position = :top # `:schema_file` by default
69
+ end
68
70
  end
69
71
  ```
70
72
 
73
+ #### annotation_position
74
+
75
+ There are a few values for this option:
76
+
77
+ - `:schema_file` -- default value, annotations get saved to a special file `app/models/annotate_solargraph_schema.rb`
78
+ - `:bottom` -- annotations are appended to the model files
79
+ - `:top` -- annotations are prepended to the model files
80
+
81
+ ### Update
82
+
83
+ To update this gem you should generate the rakefiles once again. Overwrite them.
84
+
85
+ ```sh
86
+ $ rails g annotate:solargraph:install
87
+ $ rake annotate:solargraph:remove
88
+ $ rake annotate:solargraph:generate
89
+ ```
90
+
71
91
  ## Development
72
92
 
73
93
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -9,10 +9,10 @@ module Rails
9
9
  # @return [Symbol]
10
10
  attr_reader :annotation_position
11
11
 
12
- ANNOTATION_POSITIONS = ::Set[:bottom, :top].freeze
12
+ ANNOTATION_POSITIONS = ::Set[:bottom, :top, :schema_file].freeze
13
13
 
14
14
  def initialize
15
- @annotation_position = :bottom
15
+ @annotation_position = :schema_file
16
16
  end
17
17
 
18
18
  # @param val [Symbol]
@@ -22,6 +22,10 @@ module Rails
22
22
 
23
23
  @annotation_position = val
24
24
  end
25
+
26
+ def schema_file?
27
+ @annotation_position == :schema_file
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -1,32 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
4
+
3
5
  module Rails
4
6
  module Annotate
5
7
  module Solargraph
6
8
  class Model
7
9
  using TerminalColors::Refinement
8
10
 
9
- # @return [String]
10
- ANNOTATION_START = "\n# %%<RailsAnnotateSolargraph:Start>%%"
11
- # @return [String]
12
- ANNOTATION_END = "%%<RailsAnnotateSolargraph:End>%%\n\n"
13
- # @return [Regexp]
14
- ANNOTATION_REGEXP = /#{ANNOTATION_START}.*#{ANNOTATION_END}/m.freeze
15
11
  # @return [Regexp]
16
12
  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
17
13
 
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
-
30
14
  class << self
31
15
  # @param type [Symbol, String, nil]
32
16
  # @return [String]
@@ -52,6 +36,26 @@ module Rails
52
36
  ::Object.to_s
53
37
  end
54
38
  end
39
+
40
+ # @param klass [Class]
41
+ # @return [String]
42
+ def annotation_start(klass = nil)
43
+ table_name = klass && CONFIG.schema_file? ? ":#{klass.table_name}" : ''
44
+ "\n# %%<RailsAnnotateSolargraph:Start#{table_name}>%%"
45
+ end
46
+
47
+ # @param klass [Class]
48
+ # @return [String]
49
+ def annotation_end(klass = nil)
50
+ table_name = klass && CONFIG.schema_file? ? ":#{klass.table_name}" : ''
51
+ "%%<RailsAnnotateSolargraph:End#{table_name}>%%\n\n"
52
+ end
53
+
54
+ # @param klass [Class]
55
+ # @return [Regexp]
56
+ def annotation_regexp(klass = nil)
57
+ /#{annotation_start(klass)}.*#{annotation_end(klass)}/m
58
+ end
55
59
  end
56
60
 
57
61
  # @return [String]
@@ -63,7 +67,23 @@ module Rails
63
67
  # @param klass [Class]
64
68
  def initialize(klass)
65
69
  @klass = klass
66
- @file_name = ::File.join(::Rails.root, MODEL_DIR, "#{klass.to_s.underscore}.rb")
70
+ base_file_name = CONFIG.schema_file? ? SCHEMA_FILE_NAME : "#{klass.to_s.underscore}.rb"
71
+ @file_name = ::File.join(::Rails.root, MODEL_DIR, base_file_name)
72
+ end
73
+
74
+ # @return [String]
75
+ def annotation_start
76
+ self.class.annotation_start(@klass)
77
+ end
78
+
79
+ # @return [String]
80
+ def annotation_end
81
+ self.class.annotation_end(@klass)
82
+ end
83
+
84
+ # @return [Regexp]
85
+ def annotation_regexp
86
+ self.class.annotation_regexp(@klass)
67
87
  end
68
88
 
69
89
  # @param :write [Boolean]
@@ -91,8 +111,10 @@ module Rails
91
111
  # @param :write [Boolean]
92
112
  # @return [Array<String>] Old file content followed by new content.
93
113
  def remove_annotation(write: true)
114
+ return ['', ''] unless ::File.exist?(@file_name)
115
+
94
116
  file_content = ::File.read(@file_name)
95
- new_file_content = file_content.sub(ANNOTATION_REGEXP, '')
117
+ new_file_content = file_content.sub(annotation_regexp, '')
96
118
  result = [file_content, new_file_content]
97
119
  return result unless write
98
120
  return result if file_content == new_file_content
@@ -105,7 +127,7 @@ module Rails
105
127
  def annotation
106
128
  doc_string = ::String.new
107
129
  doc_string << <<~DOC
108
- #{ANNOTATION_START}
130
+ #{annotation_start}
109
131
  # @!parse
110
132
  # class #{@klass} < #{@klass.superclass}
111
133
  DOC
@@ -129,7 +151,7 @@ module Rails
129
151
 
130
152
  doc_string << <<~DOC.chomp
131
153
  # end
132
- # #{ANNOTATION_END}
154
+ # #{annotation_end}
133
155
  DOC
134
156
  end
135
157
 
@@ -145,6 +167,7 @@ module Rails
145
167
  # @param content [String]
146
168
  # @return [void]
147
169
  def write_file(file_name, content)
170
+ ::FileUtils.touch(file_name) unless ::File.exists?(file_name)
148
171
  ::File.write(file_name, content)
149
172
  puts "modify".rjust(12).with_styles(:bold, :green) + " #{relative_file_name(file_name)}"
150
173
  end
@@ -154,35 +177,57 @@ module Rails
154
177
  @klass.table_name[..-2]
155
178
  end
156
179
 
180
+ # @param reflection [ActiveRecord::Reflection::AbstractReflection]
181
+ # @return [Class]
182
+ def reflection_class(reflection)
183
+ reflection.klass
184
+ rescue ::NameError
185
+ Object
186
+ end
187
+
188
+ # @param reflection [ActiveRecord::Reflection::AbstractReflection]
189
+ # @return [String]
190
+ def reflection_foreign_key(reflection)
191
+ reflection.try(:foreign_key) || '<unknown>'
192
+ end
193
+
194
+ # @param klass [Class]
195
+ # @return [String]
196
+ def class_table_name(klass)
197
+ klass.try(:table_name) || '<unknown>'
198
+ end
199
+
157
200
  # @param doc_string [String]
158
201
  # @param attr_name [String]
159
202
  # @param reflection [ActiveRecord::Reflection::AbstractReflection]
160
203
  # @return [void]
161
204
  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 = \
205
+ reflection_klass = reflection_class(reflection)
206
+ type_docstring, db_description = \
169
207
  case reflection
170
208
  when ::ActiveRecord::Reflection::BelongsToReflection
171
- type_docstring = reflection_class
172
- "`belongs_to` relation with `#{reflection_class}`. Database column `#{@klass.table_name}.#{reflection.foreign_key}`."
209
+ belongs_to_description(reflection_klass,
210
+ class_table_name(@klass),
211
+ reflection_foreign_key(reflection))
173
212
  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}`."
213
+ has_one_description(reflection_klass,
214
+ class_table_name(reflection_klass),
215
+ reflection_foreign_key(reflection))
176
216
  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}`."
217
+ has_many_description(reflection_klass,
218
+ class_table_name(reflection_klass),
219
+ reflection_foreign_key(reflection))
220
+ when ::ActiveRecord::Reflection::ThroughReflection
221
+ through_description(reflection)
222
+ else
223
+ [::Object.to_s, '']
179
224
  end
180
225
 
181
226
  doc_string << <<~DOC
182
- # # #{db_description}
227
+ # ##{db_description}
183
228
  # # @param val [#{type_docstring}, nil]
184
229
  # def #{attr_name}=(val); end
185
- # # #{db_description}
230
+ # ##{db_description}
186
231
  # # @return [#{type_docstring}, nil]
187
232
  # def #{attr_name}; end
188
233
  DOC
@@ -197,7 +242,7 @@ module Rails
197
242
  model_class.reflections[klass_relation_name]&.options&.[](:as)&.to_sym == attr_name.to_sym
198
243
  end
199
244
 
200
- classes_string = classes.join(', ')
245
+ classes_string = classes.empty? ? ::Object.to_s : classes.join(', ')
201
246
  doc_string << <<~DOC
202
247
  # # Polymorphic relation. Database columns `#{@klass.table_name}.#{attr_name}_id` and `#{@klass.table_name}.#{attr_name}_type`.
203
248
  # # @param val [#{classes_string}, nil]
@@ -208,6 +253,91 @@ module Rails
208
253
  DOC
209
254
  end
210
255
 
256
+ # @param reflection [ActiveRecord::Reflection::AbstractReflection]
257
+ # @return [Array<String>]
258
+ def through_description(reflection)
259
+ through_klass = reflection_class(reflection.through_reflection)
260
+
261
+ case (reflection.__send__(:delegate_reflection) rescue nil)
262
+ when ::ActiveRecord::Reflection::HasOneReflection
263
+ has_one_description(reflection_class(reflection.source_reflection),
264
+ class_table_name(through_klass),
265
+ reflection_foreign_key(reflection.source_reflection),
266
+ through: through_klass)
267
+ when ::ActiveRecord::Reflection::HasManyReflection
268
+ has_many_description(reflection_class(reflection.source_reflection),
269
+ class_table_name(through_klass),
270
+ reflection_foreign_key(reflection.source_reflection),
271
+ through: through_klass)
272
+ else
273
+ [::Object.to_s, '']
274
+ end
275
+ end
276
+
277
+ # @param through [Class, nil]
278
+ # @return [String]
279
+ def through_sentence(through = nil)
280
+ return '' unless through
281
+
282
+ " through `#{through}`"
283
+ end
284
+
285
+ # @param table_name [String]
286
+ # @param foreign_key [String]
287
+ # @param through [Class, nil]
288
+ # @return [String]
289
+ def column_description(table_name, foreign_key, through = nil)
290
+ return '' if through
291
+
292
+ " Database column `#{table_name}.#{foreign_key}`."
293
+ end
294
+
295
+ # @param relation [Symbol, String]
296
+ # @param klass [Class]
297
+ # @param table_name [String]
298
+ # @param foreign_key [String]
299
+ # @param through [Class, nil]
300
+ # @return [String]
301
+ def relation_description(relation, klass, table_name, foreign_key, through = nil)
302
+ " `#{relation}` relation with `#{klass}`#{through_sentence(through)}.#{column_description(table_name, foreign_key, through)}"
303
+ end
304
+
305
+
306
+ # @param klass [Class]
307
+ # @param table_name [String]
308
+ # @param foreign_key [String]
309
+ # @param :through [Class, nil]
310
+ # @return [Array<String>] Type docstring followed by the description of the method.
311
+ def has_many_description(klass, table_name, foreign_key, through: nil)
312
+ type_docstring = "Array<#{klass}>"
313
+ desc = relation_description(:has_many, klass, table_name, foreign_key, through)
314
+
315
+ [type_docstring, desc]
316
+ end
317
+
318
+ # @param klass [Class]
319
+ # @param table_name [String]
320
+ # @param foreign_key [String]
321
+ # @param :through [Class, nil]
322
+ # @return [Array<String>] Type docstring followed by the description of the method.
323
+ def has_one_description(klass, table_name, foreign_key, through: nil)
324
+ type_docstring = klass
325
+ desc = relation_description(:has_one, klass, table_name, foreign_key, through)
326
+
327
+ [type_docstring, desc]
328
+ end
329
+
330
+ # @param klass [Class]
331
+ # @param table_name [String]
332
+ # @param foreign_key [String]
333
+ # @return [Array<String>] Type docstring followed by the description of the method.
334
+ def belongs_to_description(klass, table_name, foreign_key)
335
+ type_docstring = klass
336
+ desc = relation_description(:belongs_to, klass, table_name, foreign_key)
337
+
338
+ [type_docstring, desc]
339
+ end
340
+
211
341
  # @param attr_type [ActiveModel::Type::Value]
212
342
  # @return [String]
213
343
  def yard_type(attr_type)
@@ -3,7 +3,7 @@
3
3
  module Rails
4
4
  module Annotate
5
5
  module Solargraph
6
- VERSION = '0.2.2'
6
+ VERSION = '0.4.0'
7
7
  end
8
8
  end
9
9
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'set'
4
+ require 'fileutils'
4
5
 
5
6
  require_relative "solargraph/version"
6
7
  require_relative "solargraph/configuration"
@@ -19,11 +20,16 @@ module Rails
19
20
  CONFIG = Configuration.new
20
21
  # @return [Set<Symbol>]
21
22
  VALID_MODIFICATION_METHODS = ::Set[:annotate, :remove_annotation].freeze
23
+ # @return [String]
24
+ SCHEMA_CLASS_NAME = 'AnnotateSolargraphSchema'
25
+ # @return [String]
26
+ SCHEMA_FILE_NAME = "annotate_solargraph_schema.rb"
22
27
 
23
28
  class << self
24
29
  # @return [Array<String>] Array of changed files.
25
30
  def generate
26
31
  title 'Generating model schema annotations'
32
+ create_schema_file
27
33
  modify_models :annotate
28
34
  end
29
35
 
@@ -49,6 +55,16 @@ module Rails
49
55
 
50
56
  include TerminalColors
51
57
 
58
+ def create_schema_file
59
+ schema_file = ::File.join(::Rails.root, MODEL_DIR, SCHEMA_FILE_NAME)
60
+ return unless CONFIG.schema_file? && !::File.exist?(schema_file)
61
+
62
+ ::FileUtils.touch(schema_file)
63
+ ::File.write schema_file, <<~SCHEMA
64
+ module AnnotateSolargraphSchema; end
65
+ SCHEMA
66
+ end
67
+
52
68
  # @param method [Symbol] Name of the method that will be called on every loaded Model
53
69
  # @return [Array<String>] Array of changed files.
54
70
  def modify_models(method)
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
18
  spec.metadata["source_code_uri"] = "https://gitlab.com/mateuszdrewniak/rails-annotate-solargraph"
19
- # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
19
+ spec.metadata["changelog_uri"] = "https://gitlab.com/mateuszdrewniak/rails-annotate-solargraph/-/raw/main/CHANGELOG.md"
20
20
 
21
21
  # Specify which files should be added to the gem when it is released.
22
22
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
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.2.2
4
+ version: 0.4.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-16 00:00:00.000000000 Z
11
+ date: 2022-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -67,6 +67,7 @@ licenses:
67
67
  metadata:
68
68
  homepage_uri: https://gitlab.com/mateuszdrewniak/rails-annotate-solargraph
69
69
  source_code_uri: https://gitlab.com/mateuszdrewniak/rails-annotate-solargraph
70
+ changelog_uri: https://gitlab.com/mateuszdrewniak/rails-annotate-solargraph/-/raw/main/CHANGELOG.md
70
71
  post_install_message:
71
72
  rdoc_options: []
72
73
  require_paths: