solargraph-rails 1.1.2 → 1.2.0

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.
data/.solargraph.yml CHANGED
@@ -2,10 +2,13 @@
2
2
  include:
3
3
  - "**/*.rb"
4
4
  exclude:
5
- - test/**/*
5
+ - spec/**/*
6
+ - lib/solargraph/rails/annotations/**/*
6
7
  - vendor/**/*
7
8
  - ".bundle/**/*"
8
- require: []
9
+ require:
10
+ # we generally assume a solargraph require has been made and don't make it explicitly
11
+ - solargraph
9
12
  domains: []
10
13
  reporters: []
11
14
  formatter:
data/CHANGELOG.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  ## Changes
4
4
 
5
+ ### v1.2
6
+
7
+ Most of these are courtesy of @grndr, with integration work by
8
+ @iftheshoefritz and @apiology
9
+
10
+ Features / fixes:
11
+ - Unlock support for Solargraph ~>0.56.0, with [major
12
+ improvements](https://github.com/castwide/solargraph/blob/master/CHANGELOG.md)
13
+ across the board
14
+ - Support writers in Rails models
15
+ - Support `gem_rbs_collection` for better quality Rails types
16
+ - Use Solargraph::Pin::DelegatedMethod to define delegate methods
17
+ - Include overloads and parameter types for ActiveRecord methods
18
+ - Improve private relation support for ActiveRecord
19
+ - Add find_by_column methods, clean up spec (Thanks, @ShadiestGoat!)
20
+ - Annotation improvements driven by testing
21
+
22
+
23
+ Internal improvements:
24
+ - Many CI, linting and testing improvements
25
+ - Extract Util.extract_option helper
26
+ - Remove Rails 5 & Rails 6 specs
27
+ - Configure test app to behave like a real app
28
+ - Use ruby/setup-ruby action and test app Gemfile for CI
29
+ - Add a plugin that generates yard docs after bundle install
30
+ - Update test pin definitions and exclusions to be Solargraph and
31
+ Rails version-focused in a single location
32
+ - Working build matrix with heavy caching
33
+ - Add typecheck workflow
34
+ - Use Solargraph::Pin::DelegatedMethod to define delegate methods
35
+ - Update README introduction
36
+ - Update model spec to expect private relation types
37
+ - Fix undefined closure for generated parameter pins
38
+ - Add Rails 7.1, 7.2 and 8.0 to test matrix
39
+ - README.md example bug fix (Thanks, @snuggs!)
40
+
5
41
  ### v1.1.2
6
42
 
7
43
  Features / fixes:
@@ -14,7 +50,6 @@ Internal improvements:
14
50
  - Use normal ruby source for extra YARD annotations (Thanks, @grncdr!)
15
51
  - Speed up CI for recent Solargraph version fresh builds
16
52
  - spec reliability fixes
17
- -
18
53
 
19
54
  ### v1.1.1
20
55
 
data/DEVELOPMENT.md CHANGED
@@ -1,19 +1,5 @@
1
1
  # Solargraph-Rails development guide
2
2
 
3
- ## Contributing
4
-
5
- 1. create fork and clone the repo
6
- 2. install gem deps `bundle install`
7
- 3. install dummy Rails app deps and build its yard cache
8
-
9
- ```
10
- $ cd spec/rails7
11
- $ bundle install && yard gems
12
- $ cd ../../
13
- ```
14
- 4. now tests should pass locally and you can try different changes
15
- 5. submit PR
16
-
17
3
  ## Debugging workflow / test matrix issues locally
18
4
 
19
5
  ```sh
@@ -24,7 +10,7 @@ act pull_request
24
10
  ## Completion coverage tracking
25
11
 
26
12
  Solargraph-Rails uses a [set of yaml files](https://github.com/iftheshoefritz/solargraph-rails/tree/master/spec/definitions) to track coverage of found completions.
27
- Those yaml files are generated at runtime from a dummy [Rails 7 app](https://github.com/iftheshoefritz/solargraph-rails/tree/master/spec/rails7).
13
+ Those yaml files are generated at runtime from a dummy Rails apps in the spec/rails* directories.
28
14
 
29
15
  The main goal is to catch any regressions in case of any change. In case a method completion is marked completed and it is not found in solargraph completions, the tests will fail.
30
16
 
@@ -48,7 +34,7 @@ What you will see in test output is reported coverage for classes that are track
48
34
  In case an improvement is made, and more completions are found then being asserted, tests will throw a warning:
49
35
 
50
36
  ```
51
- ActionDispatch::Routing::Mapper.try! is marked as skipped in spec/definitions/rails5/routes.yml, but is actually present.
37
+ ActionDispatch::Routing::Mapper.try! is marked as skipped in spec/definitions/routes.yml, but is actually present.
52
38
  Consider setting skip=false
53
39
  provides completions for ActionDispatch::Routing::Mapper
54
40
  ```
@@ -76,15 +62,18 @@ In case a new set of assertion files has to be created (for a new Rails version
76
62
  All you have to do is execute the script and pass it a path to rails app:
77
63
 
78
64
  ```
79
- ruby script/generate_definitions.rb spec/rails7
65
+ cd spec/rails8
66
+ rails g model model
67
+ rails db:drop db:create db:migrate
68
+ bundle exec ruby ../../script/generate_definitions.rb .
80
69
  ```
81
70
 
82
- Make sure to review the script and uncomment relevant parts
71
+ Move .yml files into place, then make sure to review the script and uncomment relevant parts
83
72
 
84
73
  ## Preparing a release (maintainers)
85
74
 
86
75
  1. Look up [most recent release](https://rubygems.org/gems/solargraph-rails)
87
- 2. Open up commit list(https://github.com/iftheshoefritz/solargraph-rails/compare/v1.1.1...main)
76
+ 2. Open up [commit list](https://github.com/iftheshoefritz/solargraph-rails/compare/v1.1.2...main)
88
77
  3. Update [CHANGELOG.md](./CHANGELOG.md)
89
78
  4. Flip to 'files changed view' and refine updates
90
79
  5. Bump [version](./lib/solargraph/rails/version.rb) appropriately
data/Gemfile CHANGED
@@ -1,14 +1,14 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
-
4
3
  # Kind of ugly, but works. The setup-ruby action forces gems to be installed to vendor/bundle
5
- # If we use it naively we end up with vendor/bundle and spec/rails7/vendor/bundle, which
4
+ # If we use it naively we end up with vendor/bundle and spec/rails*/vendor/bundle, which
6
5
  # breaks all the tests because docs are generated in two different directories.
7
6
  #
8
7
  # So if we just install the rails deps at the same time, we have a single cache and a single
9
8
  # directory for gems.
10
- rails_version = ENV['MATRIX_RAILS_VERSION'] || '7'
11
- instance_eval File.read(File.expand_path("spec/rails#{rails_version}/Gemfile", __dir__))
9
+ rails_version = (ENV['CI'] && ENV['MATRIX_RAILS_VERSION']) || '7.2'
10
+ rails_major_version = rails_version.split('.').first
11
+ instance_eval File.read(File.expand_path("spec/rails#{rails_major_version}/Gemfile", __dir__))
12
12
 
13
13
  solargraph_version = (ENV['CI'] && ENV['MATRIX_SOLARGRAPH_VERSION'])
14
14
 
@@ -30,7 +30,18 @@ group :development, :test do
30
30
  gem 'byebug'
31
31
  end
32
32
 
33
- if rails_version == '7'
33
+ group :development, :rubocop do
34
+ gem 'rubocop', require: false
35
+ gem 'rubocop-rake', require: false
36
+ gem 'rubocop-rspec', require: false
37
+ gem 'rubocop-performance', require: false
38
+ gem 'overcommit'
39
+ gem 'simplecov-lcov',
40
+ github: 'apiology/simplecov-lcov',
41
+ branch: 'avoid_blank_lines'
42
+ end
43
+
44
+ if rails_major_version == '7'
34
45
  # https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror
35
46
  gem "concurrent-ruby", '<=1.3.5'
36
47
  end
@@ -40,11 +51,12 @@ gemspec
40
51
 
41
52
  solargraph_force_ci_version = (ENV['CI'] && ENV['MATRIX_SOLARGRAPH_VERSION'])
42
53
 
43
- if solargraph_force_ci_version == '0.54.6.alpha'
54
+ case solargraph_force_ci_version
55
+ when '0.56.alpha'
44
56
  gem 'solargraph',
45
57
  github: 'apiology/solargraph',
46
- branch: 'v54-alpha'
47
- # path: '../solargraph'
58
+ branch: '2025-06-24'
59
+ # path: '../solargraph'
48
60
  end
49
61
 
50
62
  # Local gemfile for development tools, etc.
data/README.md CHANGED
@@ -1,46 +1,48 @@
1
1
  # Solargraph::Rails - Help solargraph with Rails
2
2
 
3
3
  ## Models
4
- Given a typical Rails model like this:
4
+ Consider pair of typical Rails models like this:
5
+
6
+ ```sh
7
+ rails g model Author lastname:string firstnames:string
8
+ rails g model Book title:string isbn:string author:belongs_to
9
+ ```
5
10
 
6
11
  ```ruby
7
- # == Schema Information
8
- #
9
- # Table name: my_books
10
- #
11
- # id :integer not null, primary key
12
- # author :string
13
- # name :string
14
- # created_at :datetime not null
15
- # updated_at :datetime not null
16
- #
17
- class MyBook < ApplicationRecord
18
- def my_method
19
- "hello"
12
+ class Author < ApplicationRecord
13
+ has_many :books
14
+
15
+ def sortable_name
16
+ "#{lastname}, #{firstnames}"
20
17
  end
18
+ end
21
19
 
22
- ...
20
+ class Book < ApplicationRecord
21
+ belongs_to :author
23
22
 
23
+ def label
24
+ [author.sortable_name, title, isbn].join("\n")
25
+ end
24
26
  end
25
27
  ```
26
28
 
27
- The various Ruby intellisense tools are ok at knowing that there is a `MyBook` constant, and some (including Solargraph) are aware that objects like `MyBook.new` have a method `.my_method`. But what about those magical dynamic attributes that ActiveRecord creates when Rails starts up? You can see these listed at the top of the file under `# == Schema Information`, the comments helpfully added by the Annotate gem.
29
+ The various Ruby intellisense tools are ok at knowing that there are `Book` and `Author` constants, and some (including Solargraph) are aware that objects like `Book.new` have a `.label` method. But what about those "magical" dynamic methods that ActiveRecord creates like `.title`, or `.author`?
28
30
 
29
- Since these attributes are only created at runtime, static analysis alone can't identify them. Your editor has no idea that these attributes exist, but they're amongst the most common things that you will work with in any Rails app.
31
+ Since these attributes are only created at runtime, a simple static analysis of the `Book` class alone can't identify them. Your editor has no idea that these attributes exist, but they're amongst the most common things that you will work with in any Rails app.
30
32
 
31
- That's where this plugin for Solargraph comes in: it parses the database schema and YARD docs of various gems to give Solargraph some extra hints. For instance database attributes:
33
+ That's where this plugin for Solargraph comes in: it understands db/schema.rb and any comments from the annotate\_models gem for models, and also supplies key annotations and Rails-specific context on top of what Solargraph pulls via YARD and RBS. As a result, you have access to database attributes:
32
34
 
33
35
  ![Go to attribute schema definition](assets/sg_rails_1_0_go_to_attribute_definition.gif)
34
36
 
35
- ... or ActiveRecord finders:
37
+ ... and ActiveRecord finders:
36
38
 
37
39
  ![ActiveRecord method support](assets/sg_rails_1_0_activerecord_support.gif)
38
40
 
39
- ... or associations:
41
+ ... and associations:
40
42
 
41
43
  ![Association support](assets/sg_rails_1_0_association_completion.gif)
42
44
 
43
- ... or routes file:
45
+ ... and routes file syntax:
44
46
 
45
47
  ![Routes file support](assets/sg_rails_1_0_routes_support.gif)
46
48
 
@@ -52,6 +54,16 @@ and more!
52
54
 
53
55
  If you add them to your Gemfile, you'll have to tell your IDE plugin to use bundler to load the right version of solargraph.
54
56
 
57
+ ### Import Rails RBS types
58
+
59
+ Use [gem\_rbs\_collection](https://github.com/ruby/gem_rbs_collection)
60
+ to install RBS types for Rails:
61
+
62
+ ```sh
63
+ rbs collection init
64
+ rbs collection install
65
+ ```
66
+
55
67
  ### Add `solargraph-rails` to your `.solargraph.yml`
56
68
 
57
69
  (if you don't have a `.solargraph.yml` in your project root, you can run `solargraph config` to add one)
@@ -71,18 +83,19 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/ifthes
71
83
 
72
84
  2. install gem deps `bundle install`
73
85
 
74
- 3. install dummy rails app deps and build the yard cache:
86
+ 3. install dummy rails app deps:
75
87
 
76
88
  ```
77
- $ cd spec/rails7
78
- $ bundle install && yard gems
79
- $ cd ../../
89
+ cd spec/rails7 && bundle install && rbs collection init && rbs collection install && cd ../../
90
+ cd spec/rails8 && bundle install && rbs collection init && rbs collection install && cd ../../
80
91
  ```
81
92
 
82
93
  4. now tests should pass locally and you can try different changes
83
94
 
84
95
  5. submit PR
85
96
 
97
+ See [DEVELOPMENT.md](./DEVELOPMENT.md) for more information
98
+
86
99
  ## License
87
100
 
88
101
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/bin/overcommit ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'overcommit' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("overcommit", "overcommit")
data/bin/rubocop ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rubocop", "rubocop")
@@ -1,5 +1,4 @@
1
1
  Bundler::Plugin.add_hook('after-install-all') do
2
- system('bundle exec yard gems')
3
2
  cmd = 'bundle exec yard gems --plugin solargraph'
4
3
  STDERR.puts("Installing yard info using #{cmd.inspect}")
5
4
  # Attempt to run yard, and if it fails, try again - sometimes YARD gets indigestion on a given gem
@@ -35,6 +35,16 @@ module Solargraph
35
35
  location:
36
36
  Solargraph::Location.new(source_map.filename, snip.range)
37
37
  )
38
+
39
+ pins <<
40
+ Util.build_public_method(
41
+ ns,
42
+ "#{name}=",
43
+ types: [ruby_type],
44
+ params: { 'value' => [ruby_type] },
45
+ location:
46
+ Solargraph::Location.new(source_map.filename, snip.range)
47
+ )
38
48
  end
39
49
 
40
50
  pins
@@ -1,12 +1,59 @@
1
1
  class ActionController::Base
2
- include ActionController::MimeResponds
3
- include ActionController::Redirecting
4
- include ActionController::Cookies
2
+ #
3
+ # NOTE: keep this list synced with new items from MODULES in action_controller/base.rb
4
+ #
5
+ # @todo pull this as literal array dynamically from
6
+ # ActionController::Base::MODULES and walk through it (and other
7
+ # cases of same pattern) to help future-proof things
8
+ #
5
9
  include AbstractController::Rendering
10
+ extend AbstractController::Rendering::ClassMethods
11
+ include AbstractController::Translation
12
+ include AbstractController::AssetPaths
13
+ include Helpers
14
+ include UrlFor
15
+ include Redirecting
16
+ include ActionView::Layouts
17
+ include Rendering
18
+ include Renderers::All
19
+ include ConditionalGet
20
+ include EtagWithTemplateDigest
21
+ include EtagWithFlash
22
+ include Caching
23
+ include MimeResponds
24
+ include ImplicitRender
25
+ include StrongParameters
26
+ include ParameterEncoding
27
+ include Cookies
28
+ include Flash
29
+ include FormBuilder
30
+ include RequestForgeryProtection
31
+ extend RequestForgeryProtection::ClassMethods
32
+ include ContentSecurityPolicy
33
+ include PermissionsPolicy
34
+ extend PermissionsPolicy::ClassMethods
35
+ include Streaming
36
+ include DataStreaming
37
+ include HttpAuthentication::Basic::ControllerMethods
38
+ extend HttpAuthentication::Basic::ControllerMethods::ClassMethods
39
+ include HttpAuthentication::Digest::ControllerMethods
40
+ include HttpAuthentication::Token::ControllerMethods
41
+ include DefaultHeaders
42
+ include Logging
43
+ extend Logging::ClassMethods
44
+ include AbstractController::Callbacks
45
+ extend AbstractController::Callbacks::ClassMethods
46
+ include Rescue
47
+ include Instrumentation
48
+ include ParamsWrapper
49
+
50
+ #
51
+ # I don't see the thinsg below in action_controller/base.rb, at least in Rails
52
+ # 7.0. Maybe they need to be moved to be under a different class?
53
+ #
6
54
  extend ActiveSupport::Callbacks::ClassMethods
7
55
  extend ActiveSupport::Rescuable::ClassMethods
8
- extend AbstractController::Callbacks::ClassMethods
9
- extend ActionController::RequestForgeryProtection::ClassMethods
56
+ include ActiveSupport::Rescuable
10
57
 
11
58
  # @return [ActionDispatch::Response]
12
59
  def response; end
@@ -27,3 +74,8 @@ class ActionController::Cookies
27
74
  # @return [ActionDispatch::Cookies::CookieJar]
28
75
  def cookies; end
29
76
  end
77
+
78
+ class ActionController::StrongParameters
79
+ # @return [ActionController::Parameters]
80
+ def params; end
81
+ 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,14 @@
1
1
  class ActiveRecord::ConnectionAdapters::SchemaStatements
2
2
  # @yieldparam [ActiveRecord::ConnectionAdapters::TableDefinition]
3
- def create_table; end
3
+ # @return [void]
4
+ def create_table(table_name, id: nil, primary_key: nil, force: false, **options); end
4
5
  # @yieldparam [ActiveRecord::ConnectionAdapters::TableDefinition]
5
- def create_join_table; end
6
+ # @param column_options [Hash]
7
+ # @return [void]
8
+ def create_join_table(table_1, table_2, column_options: {}, **options); end
6
9
  # @yieldparam [ActiveRecord::ConnectionAdapters::Table]
7
- def change_table; end
10
+ # @return [void]
11
+ def change_table(table_name, **options); end
8
12
  end
9
13
 
10
14
  # this module doesn't really exist, it's here to avoid repeating these mixins
@@ -33,6 +37,21 @@ class ActiveRecord::Base
33
37
  extend ActiveRecord::Scoping::Named::ClassMethods
34
38
  extend ActiveRecord::RelationMethods
35
39
  include ActiveRecord::Persistence
40
+ extend ActiveModel::AttributeRegistration::ClassMethods
41
+ # note: this supplies set_callback() - after Rails 7.1, this is no
42
+ # longer used and is replaced entirely by ActiveRecord::Callbacks
43
+ # below
44
+ include ActiveRecord::Callbacks
45
+ extend ActiveRecord::Callbacks::ClassMethods
46
+ extend Translation
47
+
48
+ def self.set_callback
49
+ end
50
+ end
51
+
52
+ module ActiveRecord::Validations
53
+ # @return [Boolean]
54
+ def validate; end
36
55
  end
37
56
 
38
57
  # @!override ActiveRecord::Batches#find_each
@@ -52,3 +71,5 @@ end
52
71
  # @return_single_parameter
53
72
  # @!override ActiveRecord::QueryMethods::WhereChain#associated
54
73
  # @return_single_parameter
74
+ # @!override ActiveRecord::ConnectionAdapters::SchemaStatements#create_table
75
+ # @yieldparam [ActiveRecord::ConnectionAdapters::TableDefinition]
@@ -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,3 @@
1
+ class Array
2
+ def sum; end
3
+ end
@@ -0,0 +1,2 @@
1
+ # @!override Class#subclasses
2
+ # @return [Array<Class>]
@@ -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]
@@ -24,7 +24,7 @@ class Time
24
24
 
25
25
  # @return [Time]
26
26
  def to_time; end
27
-
28
- # @return [Time]
29
- def +(other); end
30
27
  end
28
+
29
+ # @!override Time#+
30
+ # @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