Skip to content

Commit

Permalink
Initial skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
alisnic committed Mar 2, 2022
1 parent 57085bc commit 2cfd819
Show file tree
Hide file tree
Showing 1,553 changed files with 23,880 additions and 1,421 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/pkg/
/spec/reports/
/tmp/
.DS_Store

# rspec failure tracking
.rspec_status
Expand Down
79 changes: 57 additions & 22 deletions lib/solargraph-rails.rb
Original file line number Diff line number Diff line change
@@ -1,36 +1,71 @@
# frozen_string_literal: true

require 'solargraph'
require 'solargraph/rails/version'
require_relative 'solargraph/rails/pin_creator'
require_relative 'solargraph/rails/ruby_parser'
require_relative 'solargraph/rails/files_loader'
require_relative 'solargraph/rails/meta_source/association/belongs_to_matcher'
require_relative 'solargraph/rails/meta_source/association/has_many_matcher'
require_relative 'solargraph/rails/meta_source/association/has_one_matcher'
require_relative 'solargraph/rails/meta_source/association/has_and_belongs_to_many_matcher'
require 'active_support/core_ext/string/inflections'

require_relative 'solargraph/rails/util.rb'
require_relative 'solargraph/rails/schema.rb'
require_relative 'solargraph/rails/annotate.rb'
require_relative 'solargraph/rails/autoload.rb'
require_relative 'solargraph/rails/model.rb'
require_relative 'solargraph/rails/devise.rb'
require_relative 'solargraph/rails/walker.rb'
require_relative 'solargraph/rails/rails_api.rb'
require_relative 'solargraph/rails/delegate.rb'
require_relative 'solargraph/rails/storage.rb'
require_relative 'solargraph/rails/debug.rb'
require_relative 'solargraph/rails/version.rb'

module Solargraph
module Rails
class DynamicAttributes < Solargraph::Convention::Base
def global yard_map
Solargraph::Environ.new(pins: parse_models)
end
class NodeParser
extend Solargraph::Parser::Legacy::ClassMethods
end

private
class Convention < Solargraph::Convention::Base
def global(yard_map)
Solargraph::Environ.new(
pins: Solargraph::Rails::RailsApi.instance.global(yard_map)
)
rescue => error
Solargraph.logger.warn(
error.message + "\n" + error.backtrace.join("\n")
)
EMPTY_ENVIRON
end

def parse_models
def local(source_map)
pins = []
ds =
source_map.document_symbols.select do |n|
n.is_a?(Solargraph::Pin::Namespace)
end
ns = ds.first

FilesLoader.new(
Dir[File.join(Dir.pwd, 'app', 'models', '**', '*.rb')]
).each { |file, contents| pins.push *PinCreator.new(file, contents).create_pins }
return EMPTY_ENVIRON unless ns

pins
pins += run_feature { Schema.instance.process(source_map, ns) }
pins += run_feature { Annotate.instance.process(source_map, ns) }
pins += run_feature { Model.instance.process(source_map, ns) }
pins += run_feature { Storage.instance.process(source_map, ns) }
pins += run_feature { Autoload.instance.process(source_map, ns, ds) }
pins += run_feature { Devise.instance.process(source_map, ns) }
pins += run_feature { Delegate.instance.process(source_map, ns) }
pins += run_feature { RailsApi.instance.local(source_map, ns) }

Solargraph::Environ.new(pins: pins)
end

private

def run_feature(&block)
yield
rescue => error
Solargraph.logger.warn(
error.message + "\n" + error.backtrace.join("\n")
)
[]
end
end
end
end


Solargraph::Convention.register Solargraph::Rails::DynamicAttributes
Solargraph::Convention.register(Solargraph::Rails::Convention)
44 changes: 44 additions & 0 deletions lib/solargraph/rails/annotate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module Solargraph
module Rails
class Annotate
def self.instance
@instance ||= self.new
end

def self.reset
@instance = nil
end

def initialize
@schema_present = File.exist?('db/schema.rb')
end

def process(source_map, ns)
return [] if @schema_present
return [] unless source_map.filename.include?('app/models')

pins = []
walker = Walker.from_source(source_map.source)
walker.comments.each do |_, snip|
name, type = snip.text.gsub(/[\(\),:\d]/, '').split[1..2]

next unless name && type

ruby_type = Schema::RUBY_TYPES[type.to_sym]
next unless ruby_type

pins <<
Util.build_public_method(
ns,
name,
types: [ruby_type],
location:
Solargraph::Location.new(source_map.filename, snip.range)
)
end

pins
end
end
end
end
50 changes: 50 additions & 0 deletions lib/solargraph/rails/annotations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# The following comments fill some of the gaps in Solargraph's understanding of
# Rails apps. Since they're all in YARD, they get mapped in Solargraph but
# ignored at runtime.
#
# You can put this file anywhere in the project, as long as it gets included in
# the workspace maps. It's recommended that you keep it in a standalone file
# instead of pasting it into an existing one.
#
# @!parse
# class ActionController::Base
# include ActionController::MimeResponds
# include ActionController::Redirecting
# include ActionController::Cookies
# include AbstractController::Rendering
# extend ActiveSupport::Callbacks::ClassMethods
# extend ActiveSupport::Rescuable::ClassMethods
# extend AbstractController::Callbacks::ClassMethods
# extend ActionController::RequestForgeryProtection::ClassMethods
# end
# class ActionDispatch::Routing::Mapper
# include ActionDispatch::Routing::Mapper::Base
# include ActionDispatch::Routing::Mapper::HttpHelpers
# include ActionDispatch::Routing::Mapper::Redirection
# include ActionDispatch::Routing::Mapper::Scoping
# include ActionDispatch::Routing::Mapper::Concerns
# include ActionDispatch::Routing::Mapper::Resources
# include ActionDispatch::Routing::Mapper::CustomUrls
# end
# class Rails
# # @return [Rails::Application]
# def self.application; end
# end
# class Rails::Application
# # @return [ActionDispatch::Routing::RouteSet]
# def routes; end
# end
# class ActionDispatch::Routing::RouteSet
# # @yieldself [ActionDispatch::Routing::Mapper]
# def draw; end
# end
# class ActiveRecord::Base
# extend ActiveRecord::QueryMethods
# extend ActiveRecord::FinderMethods
# extend ActiveRecord::Associations::ClassMethods
# extend ActiveRecord::Inheritance::ClassMethods
# extend ActiveRecord::ModelSchema::ClassMethods
# extend ActiveRecord::Transactions::ClassMethods
# extend ActiveRecord::Scoping::Named::ClassMethods
# include ActiveRecord::Persistence
# end
53 changes: 53 additions & 0 deletions lib/solargraph/rails/autoload.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module Solargraph
module Rails
class Autoload
def self.instance
@instance ||= self.new
end

def process(source_map, ns, ds)
return [] unless ds.size == 1 && ns.path.include?('::')
Solargraph.logger.debug(
"[Rails][Autoload] seeding class tree for #{ns.path}"
)

root_ns = source_map.pins.find { |p| p.path == '' }
namespace_stubs(root_ns, ns)
end

def namespace_stubs(root_ns, ns)
parts = ns.path.split('::')

candidates =
parts
.each_with_index
.reduce([]) { |acc, (_, i)| acc + [parts[0..i].join('::')] }
.reject { |el| el == ns.path }

previous_ns = root_ns
pins = []

parts[0..-2].each_with_index do |name, i|
gates = candidates[0..i].reverse + ['']
path = gates.first
next if path == ns.path

previous_ns =
Solargraph::Pin::Namespace.new(
type: :class,
location: ns.location,
closure: previous_ns,
name: name,
comments: ns.comments,
visibility: :public,
gates: gates[1..-1]
)

pins << previous_ns
end

pins
end
end
end
end
30 changes: 30 additions & 0 deletions lib/solargraph/rails/debug.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Solargraph
module Rails
class Debug
def self.run(query=nil)
self.new.run(query)
end

def run(query)
Solargraph.logger.level = Logger::DEBUG

api_map = Solargraph::ApiMap.load('./')

puts "Ruby version: #{RUBY_VERSION}"
puts "Solargraph version: #{Solargraph::VERSION}"
puts "Solargraph ARC version: #{Solargraph::Rails::VERSION}"

return unless query

puts "Known methods for #{query}"

pin = api_map.pins.find {|p| p.path == query }
return unless pin

api_map.get_complex_type_methods(pin.return_type).each do |pin|
puts "- #{pin.path}"
end
end
end
end
end
38 changes: 38 additions & 0 deletions lib/solargraph/rails/delegate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module Solargraph
module Rails
class Delegate
def self.instance
@instance ||= self.new
end

def process(source_map, ns)
return [] unless source_map.code.include?('delegate')

walker = Walker.from_source(source_map.source)
pins = []

walker.on :send, [nil, :delegate] do |ast|
methods =
ast.children[2..-1]
.map { |c| c.children.first }
.select { |s| s.is_a?(Symbol) }

methods.each do |meth|
pins <<
Util.build_public_method(
ns,
meth.to_s,
location: Util.build_location(ast, ns.filename)
)
end
end

walker.walk
Solargraph.logger.debug(
"[ARC][Delegate] added #{pins.map(&:name)} to #{ns.path}"
)
pins
end
end
end
end
48 changes: 48 additions & 0 deletions lib/solargraph/rails/devise.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module Solargraph
module Rails
class Devise
def self.instance
@instance ||= self.new
end

def process(source_map, ns)
if source_map.filename.include?('app/models')
process_model(source_map.source, ns)
else
[]
end
end

private

def process_model(source, ns)
walker = Walker.from_source(source)
pins = []

walker.on :send, [nil, :devise] do |ast|
modules =
ast.children[2..-1]
.map { |c| c.children.first }
.select { |s| s.is_a?(Symbol) }

modules.each do |mod|
pins <<
Util.build_module_include(
ns,
"Devise::Models::#{mod.to_s.capitalize}",
Util.build_location(ast, ns.filename)
)
end
end

walker.walk
if pins.any?
Solargraph.logger.debug(
"[ARC][Devise] added #{pins.map(&:name)} to #{ns.path}"
)
end
pins
end
end
end
end
16 changes: 0 additions & 16 deletions lib/solargraph/rails/files_loader.rb

This file was deleted.

Loading

0 comments on commit 2cfd819

Please sign in to comment.