solargraph-rails 1.1.2 → 1.2.1

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linter.yml +94 -0
  3. data/.github/workflows/test.yml +185 -0
  4. data/.github/workflows/typecheck.yml +83 -0
  5. data/.overcommit.yml +51 -0
  6. data/.rubocop.yml +334 -0
  7. data/.rubocop_todo.yml +784 -0
  8. data/.solargraph.yml +8 -2
  9. data/CHANGELOG.md +51 -1
  10. data/DEVELOPMENT.md +8 -19
  11. data/Gemfile +22 -9
  12. data/README.md +39 -25
  13. data/bin/overcommit +27 -0
  14. data/bin/rubocop +27 -0
  15. data/ci/auto_yard/plugins.rb +0 -1
  16. data/lib/solargraph/rails/annotate.rb +10 -0
  17. data/lib/solargraph/rails/annotations/action_controller.rb +57 -5
  18. data/lib/solargraph/rails/annotations/action_text.rb +11 -0
  19. data/lib/solargraph/rails/annotations/active_job.rb +276 -0
  20. data/lib/solargraph/rails/annotations/active_model.rb +18 -0
  21. data/lib/solargraph/rails/annotations/active_record.rb +54 -3
  22. data/lib/solargraph/rails/annotations/active_storage.rb +113 -0
  23. data/lib/solargraph/rails/annotations/active_support.rb +3 -0
  24. data/lib/solargraph/rails/annotations/array.rb +7 -0
  25. data/lib/solargraph/rails/annotations/class.rb +2 -0
  26. data/lib/solargraph/rails/annotations/date.rb +8 -0
  27. data/lib/solargraph/rails/annotations/module.rb +13 -0
  28. data/lib/solargraph/rails/annotations/object.rb +3 -0
  29. data/lib/solargraph/rails/annotations/rails.rb +4 -0
  30. data/lib/solargraph/rails/annotations/time.rb +6 -2
  31. data/lib/solargraph/rails/delegate.rb +44 -10
  32. data/lib/solargraph/rails/model.rb +252 -31
  33. data/lib/solargraph/rails/rails_api.rb +2 -2
  34. data/lib/solargraph/rails/schema.rb +27 -8
  35. data/lib/solargraph/rails/util.rb +45 -4
  36. data/lib/solargraph/rails/version.rb +1 -1
  37. data/lib/solargraph-rails.rb +1 -1
  38. data/script/generate_definitions.rb +49 -24
  39. data/solargraph-rails.gemspec +14 -5
  40. metadata +64 -17
  41. data/.github/workflows/ruby.yml +0 -76
@@ -0,0 +1,276 @@
1
+ module ActiveJob
2
+ module Callbacks
3
+ # These methods will be included into any Active Job object, adding
4
+ # callbacks for +perform+ and +enqueue+ methods.
5
+ module ClassMethods
6
+ # Defines a callback that will get called right before the
7
+ # job's perform method is executed.
8
+ #
9
+ # class VideoProcessJob < ActiveJob::Base
10
+ # queue_as :default
11
+ #
12
+ # before_perform do |job|
13
+ # UserMailer.notify_video_started_processing(job.arguments.first)
14
+ # end
15
+ #
16
+ # def perform(video_id)
17
+ # Video.find(video_id).process
18
+ # end
19
+ # end
20
+ #
21
+ #
22
+ # @yieldreturn [void]
23
+ # @return [void]
24
+ def before_perform(*filters, &blk); end
25
+
26
+ # Defines a callback that will get called right after the
27
+ # job's perform method has finished.
28
+ #
29
+ # class VideoProcessJob < ActiveJob::Base
30
+ # queue_as :default
31
+ #
32
+ # after_perform do |job|
33
+ # UserMailer.notify_video_processed(job.arguments.first)
34
+ # end
35
+ #
36
+ # def perform(video_id)
37
+ # Video.find(video_id).process
38
+ # end
39
+ # end
40
+ #
41
+ # @yieldreturn [void]
42
+ # @return [void]
43
+ def after_perform(*filters, &blk); end
44
+
45
+ # Defines a callback that will get called around the job's perform method.
46
+ #
47
+ # class VideoProcessJob < ActiveJob::Base
48
+ # queue_as :default
49
+ #
50
+ # around_perform do |job, block|
51
+ # UserMailer.notify_video_started_processing(job.arguments.first)
52
+ # block.call
53
+ # UserMailer.notify_video_processed(job.arguments.first)
54
+ # end
55
+ #
56
+ # def perform(video_id)
57
+ # Video.find(video_id).process
58
+ # end
59
+ # end
60
+ #
61
+ # You can access the return value of the job only if the execution wasn't halted.
62
+ #
63
+ # class VideoProcessJob < ActiveJob::Base
64
+ # around_perform do |job, block|
65
+ # value = block.call
66
+ # puts value # => "Hello World!"
67
+ # end
68
+ #
69
+ # def perform
70
+ # "Hello World!"
71
+ # end
72
+ # end
73
+ #
74
+ # @yieldreturn [void]
75
+ # @return [void]
76
+ def around_perform(*filters, &blk); end
77
+
78
+ # Defines a callback that will get called right before the
79
+ # job is enqueued.
80
+ #
81
+ # class VideoProcessJob < ActiveJob::Base
82
+ # queue_as :default
83
+ #
84
+ # before_enqueue do |job|
85
+ # $statsd.increment "enqueue-video-job.try"
86
+ # end
87
+ #
88
+ # def perform(video_id)
89
+ # Video.find(video_id).process
90
+ # end
91
+ # end
92
+ #
93
+ # @yieldreturn [void]
94
+ # @return [void]
95
+ def before_enqueue(*filters, &blk); end
96
+
97
+ # Defines a callback that will get called right after the
98
+ # job is enqueued.
99
+ #
100
+ # class VideoProcessJob < ActiveJob::Base
101
+ # queue_as :default
102
+ #
103
+ # after_enqueue do |job|
104
+ # $statsd.increment "enqueue-video-job.success"
105
+ # end
106
+ #
107
+ # def perform(video_id)
108
+ # Video.find(video_id).process
109
+ # end
110
+ # end
111
+ #
112
+ # @yieldreturn [void]
113
+ # @return [void]
114
+ def after_enqueue(*filters, &blk); end
115
+
116
+ # Defines a callback that will get called around the enqueuing
117
+ # of the job.
118
+ #
119
+ # class VideoProcessJob < ActiveJob::Base
120
+ # queue_as :default
121
+ #
122
+ # around_enqueue do |job, block|
123
+ # $statsd.time "video-job.process" do
124
+ # block.call
125
+ # end
126
+ # end
127
+ #
128
+ # def perform(video_id)
129
+ # Video.find(video_id).process
130
+ # end
131
+ # end
132
+ #
133
+ # @yieldreturn [void]
134
+ # @return [void]
135
+ def around_enqueue(*filters, &blk); end
136
+ end
137
+ end
138
+
139
+ module Exceptions
140
+ module ClassMethods
141
+ # Catch the exception and reschedule job for re-execution after so many seconds, for a specific number of attempts.
142
+ # If the exception keeps getting raised beyond the specified number of attempts, the exception is allowed to
143
+ # bubble up to the underlying queuing system, which may have its own retry mechanism or place it in a
144
+ # holding queue for inspection.
145
+ #
146
+ # You can also pass a block that'll be invoked if the retry attempts fail for custom logic rather than letting
147
+ # the exception bubble up. This block is yielded with the job instance as the first and the error instance as the second parameter.
148
+ #
149
+ # ==== Options
150
+ # * <tt>:wait</tt> - Re-enqueues the job with a delay specified either in seconds (default: 3 seconds),
151
+ # as a computing proc that takes the number of executions so far as an argument, or as a symbol reference of
152
+ # <tt>:exponentially_longer</tt>, which applies the wait algorithm of <tt>((executions**4) + (Kernel.rand * (executions**4) * jitter)) + 2</tt>
153
+ # (first wait ~3s, then ~18s, then ~83s, etc)
154
+ # * <tt>:attempts</tt> - Re-enqueues the job the specified number of times (default: 5 attempts) or a symbol reference of <tt>:unlimited</tt>
155
+ # to retry the job until it succeeds
156
+ # * <tt>:queue</tt> - Re-enqueues the job on a different queue
157
+ # * <tt>:priority</tt> - Re-enqueues the job with a different priority
158
+ # * <tt>:jitter</tt> - A random delay of wait time used when calculating backoff. The default is 15% (0.15) which represents the upper bound of possible wait time (expressed as a percentage)
159
+ #
160
+ # ==== Examples
161
+ #
162
+ # class RemoteServiceJob < ActiveJob::Base
163
+ # retry_on CustomAppException # defaults to ~3s wait, 5 attempts
164
+ # retry_on AnotherCustomAppException, wait: ->(executions) { executions * 2 }
165
+ # retry_on CustomInfrastructureException, wait: 5.minutes, attempts: :unlimited
166
+ #
167
+ # retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
168
+ # retry_on Net::OpenTimeout, Timeout::Error, wait: :exponentially_longer, attempts: 10 # retries at most 10 times for Net::OpenTimeout and Timeout::Error combined
169
+ # # To retry at most 10 times for each individual exception:
170
+ # # retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 10
171
+ # # retry_on Net::ReadTimeout, wait: 5.seconds, jitter: 0.30, attempts: 10
172
+ # # retry_on Timeout::Error, wait: :exponentially_longer, attempts: 10
173
+ #
174
+ # retry_on(YetAnotherCustomAppException) do |job, error|
175
+ # ExceptionNotifier.caught(error)
176
+ # end
177
+ #
178
+ # def perform(*args)
179
+ # # Might raise CustomAppException, AnotherCustomAppException, or YetAnotherCustomAppException for something domain specific
180
+ # # Might raise ActiveRecord::Deadlocked when a local db deadlock is detected
181
+ # # Might raise Net::OpenTimeout or Timeout::Error when the remote service is down
182
+ # end
183
+ # end
184
+ #
185
+ # @yieldreturn [void]
186
+ # @return [void]
187
+ def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil, jitter: JITTER_DEFAULT); end
188
+
189
+ # Discard the job with no attempts to retry, if the exception is raised. This is useful when the subject of the job,
190
+ # like an Active Record, is no longer available, and the job is thus no longer relevant.
191
+ #
192
+ # You can also pass a block that'll be invoked. This block is yielded with the job instance as the first and the error instance as the second parameter.
193
+ #
194
+ # ==== Example
195
+ #
196
+ # class SearchIndexingJob < ActiveJob::Base
197
+ # discard_on ActiveJob::DeserializationError
198
+ # discard_on(CustomAppException) do |job, error|
199
+ # ExceptionNotifier.caught(error)
200
+ # end
201
+ #
202
+ # def perform(record)
203
+ # # Will raise ActiveJob::DeserializationError if the record can't be deserialized
204
+ # # Might raise CustomAppException for something domain specific
205
+ # end
206
+ # end
207
+ #
208
+ # @yieldreturn [void]
209
+ # @return [void]
210
+ def discard_on(*exceptions); end
211
+ end
212
+ end
213
+
214
+ module QueueName
215
+ extend ActiveSupport::Concern
216
+
217
+ # Includes the ability to override the default queue name and prefix.
218
+ module ClassMethods
219
+ mattr_accessor :default_queue_name, default: "default"
220
+
221
+ # Specifies the name of the queue to process the job on.
222
+ #
223
+ # class PublishToFeedJob < ActiveJob::Base
224
+ # queue_as :feeds
225
+ #
226
+ # def perform(post)
227
+ # post.to_feed!
228
+ # end
229
+ # end
230
+ #
231
+ # Can be given a block that will evaluate in the context of the job
232
+ # so that a dynamic queue name can be applied:
233
+ #
234
+ # class PublishToFeedJob < ApplicationJob
235
+ # queue_as do
236
+ # post = self.arguments.first
237
+ #
238
+ # if post.paid?
239
+ # :paid_feeds
240
+ # else
241
+ # :feeds
242
+ # end
243
+ # end
244
+ #
245
+ # def perform(post)
246
+ # post.to_feed!
247
+ # end
248
+ # end
249
+ #
250
+ # @return [void]
251
+ def queue_as(part_name = nil, &block); end
252
+ end
253
+ end
254
+
255
+ module QueuePriority
256
+ extend ActiveSupport::Concern
257
+
258
+ # Includes the ability to override the default queue priority.
259
+ module ClassMethods
260
+ # Specifies the priority of the queue to create the job with.
261
+ #
262
+ # class PublishToFeedJob < ActiveJob::Base
263
+ # queue_with_priority 50
264
+ #
265
+ # def perform(post)
266
+ # post.to_feed!
267
+ # end
268
+ # end
269
+ #
270
+ # Specify either an argument or a block.
271
+ #
272
+ # @return [void]
273
+ def queue_with_priority(priority = nil, &block); end
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,18 @@
1
+ module ActiveModel
2
+ module Translation
3
+ # @param options [Hash] options to customize the output
4
+ # @param attribute [Symbol, String] the name of the attribute
5
+ # @return [String]
6
+ def human_attribute_name(attribute, options = {}); end
7
+ end
8
+
9
+ module Naming
10
+ # @return [ActiveModel::Name] the model name for the class
11
+ def model_name; end
12
+ end
13
+
14
+ module Validations
15
+ # @return [Boolean]
16
+ def validate; end
17
+ end
18
+ end
@@ -1,10 +1,25 @@
1
+ require 'active_record'
2
+
1
3
  class ActiveRecord::ConnectionAdapters::SchemaStatements
2
4
  # @yieldparam [ActiveRecord::ConnectionAdapters::TableDefinition]
3
- def create_table; end
5
+ # @return [void]
6
+ def create_table(table_name, id: nil, primary_key: nil, force: false, **options); end
4
7
  # @yieldparam [ActiveRecord::ConnectionAdapters::TableDefinition]
5
- def create_join_table; end
8
+ # @param table_1 [String, Symbol]
9
+ # @param table_2 [String, Symbol]
10
+ # @param column_options [Hash]
11
+ # @param options [Hash{Symbol => undefined}]
12
+ # @return [void]
13
+ def create_join_table(table_1, table_2, column_options: {}, **options); end
6
14
  # @yieldparam [ActiveRecord::ConnectionAdapters::Table]
7
- def change_table; end
15
+ # @return [void]
16
+ def change_table(table_name, **options); end
17
+ end
18
+
19
+ module ActiveRecord::Core
20
+ # @param methods [Symbol]
21
+ # @return [ActiveSupport::HashWithIndifferentAccess<Symbol>]
22
+ def slice(*methods); end
8
23
  end
9
24
 
10
25
  # this module doesn't really exist, it's here to avoid repeating these mixins
@@ -33,6 +48,40 @@ class ActiveRecord::Base
33
48
  extend ActiveRecord::Scoping::Named::ClassMethods
34
49
  extend ActiveRecord::RelationMethods
35
50
  include ActiveRecord::Persistence
51
+ extend ActiveModel::AttributeRegistration::ClassMethods
52
+ # note: this supplies set_callback() - after Rails 7.1, this is no
53
+ # longer used and is replaced entirely by ActiveRecord::Callbacks
54
+ # below
55
+ include ActiveRecord::Callbacks
56
+ extend ActiveRecord::Callbacks::ClassMethods
57
+ extend ActiveRecord::Translation
58
+
59
+ # copied from .gem_rbs_collection/activestorage/7.0/lib/engine.rbs
60
+ # which for some reason does not get included
61
+ include ::ActiveStorage::Attached::Model
62
+ extend ::ActiveStorage::Attached::Model::ClassMethods
63
+ include ::ActiveStorage::Reflection::ActiveRecordExtensions
64
+
65
+ class << self
66
+ # included in ActiveRecordExtensions
67
+ # @return [Hash{String => ActiveStorage::Reflection::HasOneAttachedReflection, ActiveStorage::Reflection::HasManyAttachedReflection}]
68
+ attr_accessor :attachment_reflections
69
+ end
70
+
71
+ extend ::ActiveStorage::Reflection::ActiveRecordExtensions::ClassMethods
72
+
73
+ def self.set_callback; end
74
+
75
+ # @return [:activerecord]
76
+ def self.i18n_scope; end
77
+
78
+ # @return [self]
79
+ def reload(); end
80
+ end
81
+
82
+ module ActiveRecord::Validations
83
+ # @return [Boolean]
84
+ def validate(); end
36
85
  end
37
86
 
38
87
  # @!override ActiveRecord::Batches#find_each
@@ -52,3 +101,5 @@ end
52
101
  # @return_single_parameter
53
102
  # @!override ActiveRecord::QueryMethods::WhereChain#associated
54
103
  # @return_single_parameter
104
+ # @!override ActiveRecord::ConnectionAdapters::SchemaStatements#create_table
105
+ # @yieldparam [ActiveRecord::ConnectionAdapters::TableDefinition]
@@ -0,0 +1,113 @@
1
+ module ActiveStorage
2
+ # Provides the class-level DSL for declaring an Active Record model's attachments.
3
+ module Attached
4
+ module Model
5
+ # NOTE: the below would normally be in a ClassMethods module -
6
+ # but Solargraph currently uses yard-activesupport-concern,
7
+ # which jams the method in as class methods under this module.
8
+ #
9
+ # Specifies the relation between a single attachment and the model.
10
+ #
11
+ # class User < ApplicationRecord
12
+ # has_one_attached :avatar
13
+ # end
14
+ #
15
+ # There is no column defined on the model side, Active Storage takes
16
+ # care of the mapping between your records and the attachment.
17
+ #
18
+ # To avoid N+1 queries, you can include the attached blobs in your query like so:
19
+ #
20
+ # User.with_attached_avatar
21
+ #
22
+ # Under the covers, this relationship is implemented as a +has_one+ association to a
23
+ # ActiveStorage::Attachment record and a +has_one-through+ association to a
24
+ # ActiveStorage::Blob record. These associations are available as +avatar_attachment+
25
+ # and +avatar_blob+. But you shouldn't need to work with these associations directly in
26
+ # most circumstances.
27
+ #
28
+ # The system has been designed to having you go through the ActiveStorage::Attached::One
29
+ # proxy that provides the dynamic proxy to the associations and factory methods, like +attach+.
30
+ #
31
+ # If the +:dependent+ option isn't set, the attachment will be purged
32
+ # (i.e. destroyed) whenever the record is destroyed.
33
+ #
34
+ # If you need the attachment to use a service which differs from the globally configured one,
35
+ # pass the +:service+ option. For instance:
36
+ #
37
+ # class User < ActiveRecord::Base
38
+ # has_one_attached :avatar, service: :s3
39
+ # end
40
+ #
41
+ # If you need to enable +strict_loading+ to prevent lazy loading of attachment,
42
+ # pass the +:strict_loading+ option. You can do:
43
+ #
44
+ # class User < ApplicationRecord
45
+ # has_one_attached :avatar, strict_loading: true
46
+ # end
47
+ #
48
+
49
+ # @param name [String, Symbol]
50
+ # @param dependent [Symbol] the action to take on the attachment when the record is destroyed
51
+ # @param strict_loading [Boolean]
52
+ # @param service [String, Symbol, nil] the service to use for the attachment
53
+ # @return [void]
54
+ def self.has_one_attached(name, dependent: :default, service: nil, strict_loading: false); end
55
+
56
+ # Specifies the relation between multiple attachments and the model.
57
+ #
58
+ # class Gallery < ApplicationRecord
59
+ # has_many_attached :photos
60
+ # end
61
+ #
62
+ # There are no columns defined on the model side, Active Storage takes
63
+ # care of the mapping between your records and the attachments.
64
+ #
65
+ # To avoid N+1 queries, you can include the attached blobs in your query like so:
66
+ #
67
+ # Gallery.where(user: Current.user).with_attached_photos
68
+ #
69
+ # Under the covers, this relationship is implemented as a +has_many+ association to a
70
+ # ActiveStorage::Attachment record and a +has_many-through+ association to a
71
+ # ActiveStorage::Blob record. These associations are available as +photos_attachments+
72
+ # and +photos_blobs+. But you shouldn't need to work with these associations directly in
73
+ # most circumstances.
74
+ #
75
+ # The system has been designed to having you go through the ActiveStorage::Attached::Many
76
+ # proxy that provides the dynamic proxy to the associations and factory methods, like +#attach+.
77
+ #
78
+ # If the +:dependent+ option isn't set, all the attachments will be purged
79
+ # (i.e. destroyed) whenever the record is destroyed.
80
+ #
81
+ # If you need the attachment to use a service which differs from the globally configured one,
82
+ # pass the +:service+ option. For instance:
83
+ #
84
+ # class Gallery < ActiveRecord::Base
85
+ # has_many_attached :photos, service: :s3
86
+ # end
87
+ #
88
+ # If you need to enable +strict_loading+ to prevent lazy loading of attachments,
89
+ # pass the +:strict_loading+ option. You can do:
90
+ #
91
+ # class Gallery < ApplicationRecord
92
+ # has_many_attached :photos, strict_loading: true
93
+ # end
94
+ #
95
+ # @param name [String, Symbol]
96
+ # @param dependent [Symbol] the action to take on the attachment when the record is destroyed
97
+ # @param strict_loading [Boolean]
98
+ # @param service [String, Symbol, nil] the service to use for the attachment
99
+ # @return [void]
100
+ def self.has_many_attached(name, dependent: :default, service: nil, strict_loading: false); end
101
+
102
+ # @return [void]
103
+ def self.attachment_changes(); end
104
+
105
+ # @return [Boolean]
106
+ def self.changed_for_autosave?; end
107
+
108
+ # @param lock [BasicObject]
109
+ # @return [self]
110
+ def self.reload(lock = false); end
111
+ end
112
+ end
113
+ end
@@ -3,3 +3,6 @@
3
3
 
4
4
  # @!override ActiveSupport::DescendantsTracker#subclasses
5
5
  # @return [Array<Class>]
6
+
7
+ # @!override ActiveSupport::DescendantsTracker::ReloadedClassesFiltering#subclasses
8
+ # @return [Array<Class>]
@@ -0,0 +1,7 @@
1
+ class Array
2
+ def sum; end
3
+
4
+ # @param format [Symbol]
5
+ # @return [String]
6
+ def to_formatted_s(format = :some_default); end
7
+ end
@@ -0,0 +1,2 @@
1
+ # @!override Class#subclasses
2
+ # @return [Array<Class>]
@@ -23,9 +23,17 @@ class Date
23
23
 
24
24
  # @return [::Time]
25
25
  def to_time; end
26
+
27
+ # @param format [Symbol]
28
+ # @return [String]
29
+ def to_formatted_s(format = :some_default); end
26
30
  end
27
31
 
28
32
  class DateTime
29
33
  # @return [String]
30
34
  def readable_inspect; end
35
+
36
+ # @param format [Symbol]
37
+ # @return [String]
38
+ def to_formatted_s(format = :some_default); end
31
39
  end
@@ -0,0 +1,13 @@
1
+ class Module
2
+ # @return [self, nil]
3
+ def self.presence; end
4
+
5
+ # @return [self, nil]
6
+ def self.presence_in(x); end
7
+
8
+ # @return [self, nil]
9
+ def presence; end
10
+
11
+ # @return [self, nil]
12
+ def presence_in(x); end
13
+ end
@@ -5,3 +5,6 @@ class Object
5
5
  # @return [self, nil]
6
6
  def presence_in(x); end
7
7
  end
8
+
9
+ # @!override Object#present?
10
+ # @return [Boolean]
@@ -11,4 +11,8 @@ end
11
11
  class Rails::Application
12
12
  # @return [ActionDispatch::Routing::RouteSet]
13
13
  def routes; end
14
+ # @return [Rails::Application::Configuration]
15
+ def config; end
16
+ # @return [Rails::Application::Configuration]
17
+ def self.config; end
14
18
  end
@@ -25,6 +25,10 @@ class Time
25
25
  # @return [Time]
26
26
  def to_time; end
27
27
 
28
- # @return [Time]
29
- def +(other); end
28
+ # @param format [Symbol]
29
+ # @return [String]
30
+ def to_formatted_s(format = :some_default); end
30
31
  end
32
+
33
+ # @!override Time#+
34
+ # @return [Time]
@@ -1,3 +1,5 @@
1
+ require 'parser'
2
+
1
3
  module Solargraph
2
4
  module Rails
3
5
  class Delegate
@@ -5,25 +7,57 @@ module Solargraph
5
7
  @instance ||= self.new
6
8
  end
7
9
 
10
+ def self.supported?
11
+ Solargraph::Pin.const_defined?(:DelegatedMethod)
12
+ end
13
+
8
14
  def process(source_map, ns)
15
+ return [] unless self.class.supported?
9
16
  return [] unless source_map.code.include?('delegate')
10
17
 
11
18
  walker = Walker.from_source(source_map.source)
12
19
  pins = []
13
20
 
14
21
  walker.on :send, [nil, :delegate] do |ast|
15
- methods =
16
- ast.children[2..-1]
17
- .map { |c| c.children.first }
18
- .select { |s| s.is_a?(Symbol) }
22
+ last_child = ast.children[-1]
23
+ next unless last_child.instance_of?(::Parser::AST::Node) && last_child.type == :hash
24
+
25
+ methods = ast.children[2...-1].select { |c| c.type == :sym }
26
+
27
+ delegate_node = Util.extract_option(ast, :to)
28
+ next unless delegate_node
29
+
30
+ chain = if delegate_node.type == :sym
31
+ # `delegate ..., to: :bar` means call the #bar method to get the delegate object
32
+ call = Solargraph::Source::Chain::Call.new(delegate_node.children[0].to_s)
33
+ Solargraph::Source::Chain.new([call], delegate_node)
34
+ else
35
+ # for any other type of delegate, we create a chain from the AST node
36
+ Solargraph::Parser::Legacy::NodeChainer.chain(delegate_node, ns.filename)
37
+ end
38
+
39
+ prefix_node = Util.extract_option(ast, :prefix)
40
+
41
+ prefix = nil
42
+ if prefix_node
43
+ if prefix_node.type == :sym
44
+ prefix = prefix_node.children[0]
45
+ elsif prefix_node.type == :true && delegate_node.type == :sym
46
+ prefix = delegate_node.children[0]
47
+ end
48
+ end
19
49
 
50
+ location = Util.build_location(delegate_node, ns.filename)
20
51
  methods.each do |meth|
21
- pins <<
22
- Util.build_public_method(
23
- ns,
24
- meth.to_s,
25
- location: Util.build_location(ast, ns.filename)
26
- )
52
+ method_name = meth.children[0]
53
+ pins << Solargraph::Pin::DelegatedMethod.new(
54
+ closure: ns,
55
+ scope: :instance,
56
+ name: [prefix, method_name].select(&:itself).join("_"),
57
+ node: meth,
58
+ receiver: chain,
59
+ receiver_method_name: method_name.to_s,
60
+ )
27
61
  end
28
62
  end
29
63