Skip to content

Commit

Permalink
Merge pull request iftheshoefritz#40 from grncdr/catchup
Browse files Browse the repository at this point in the history
Various improvements
  • Loading branch information
alisnic authored Sep 5, 2022
2 parents a5685cc + 3962892 commit 1b322dd
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 48 deletions.
2 changes: 1 addition & 1 deletion lib/solargraph/rails/annotate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def initialize

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

pins = []
walker = Walker.from_source(source_map.source)
Expand Down
2 changes: 1 addition & 1 deletion lib/solargraph/rails/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def self.instance
end

def process(source_map, ns)
if source_map.filename.include?('app/models')
if Model.valid_filename?(source_map.filename)
process_model(source_map.source, ns)
else
[]
Expand Down
6 changes: 5 additions & 1 deletion lib/solargraph/rails/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ def self.instance
@instance ||= self.new
end

def self.valid_filename?(filename)
filename.include?('app/models')
end

def process(source_map, ns)
return [] unless source_map.filename.include?('app/models')
return [] unless self.class.valid_filename?(source_map.filename)

walker = Walker.from_source(source_map.source)
pins = []
Expand Down
30 changes: 23 additions & 7 deletions lib/solargraph/rails/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ def initialize

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

table_name = infer_table_name(ns)
table = schema[table_name]
table = find_table(source_map, ns)

return [] unless table

Expand Down Expand Up @@ -71,10 +70,27 @@ def schema
end
end

# TODO: support custom table names, by parsing `self.table_name = ` invokations
# inside model
def infer_table_name(ns)
ns.name.underscore.pluralize
def find_table(source_map, ns)
table_name = nil
walker = Walker.from_source(source_map.source)
walker.on :send, [:self, :table_name=, :str] do |ast|
table_name = ast.children.last.children.first
end
walker.walk

# always use explicit table name if present
return schema[table_name] if table_name

infer_table_names(ns).filter_map { |table_name| schema[table_name] }.first
end

def infer_table_names(ns)
table_name = ns.name.tableize
if ns.namespace.present?
[ns.path.tableize.tr('/', '_'), table_name]
else
[table_name]
end
end

def extract_schema(ast)
Expand Down
2 changes: 1 addition & 1 deletion lib/solargraph/rails/storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def self.instance
end

def process(source_map, ns)
return [] unless source_map.filename.include?('app/models')
return [] unless Model.valid_filename?(source_map.filename)

walker = Walker.from_source(source_map.source)
pins = []
Expand Down
78 changes: 41 additions & 37 deletions lib/solargraph/rails/walker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,51 @@ module Solargraph
module Rails
class Walker
class Hook
attr_reader :args, :proc, :node_type
attr_reader :node_type
def initialize(node_type, args, &block)
@node_type = node_type
@args = args
@proc = Proc.new(&block)
end

def visit(node)
return unless matches?(node)

if @proc.arity == 1
@proc.call(node)
elsif @proc.arity == 2
walker = Walker.new(node)
@proc.call(node, walker)
walker.walk
end
end

private

def matches?(node)
return unless node.type == node_type
return unless node.children
return true if @args.empty?

a_child_matches = node.children.first.is_a?(::Parser::AST::Node) && node.children.any? do |child|
child.is_a?(::Parser::AST::Node) &&
match_children(child.children, @args[1..-1])
end

return true if a_child_matches

match_children(node.children)
end

def match_children(children, args = @args)
args.each_with_index.all? do |arg, i|
if children[i].is_a?(::Parser::AST::Node)
children[i].type == arg
else
children[i] == arg
end
end
end
end

# https://github.com/castwide/solargraph/issues/522
Expand Down Expand Up @@ -45,45 +84,10 @@ def walk
def traverse(node)
return unless node.is_a?(::Parser::AST::Node)

@hooks[node.type].each { |hook| try_match(node, hook) }
@hooks[node.type].each { |hook| hook.visit(node) }

node.children.each { |child| traverse(child) }
end

def try_match(node, hook)
return unless node.type == hook.node_type
return unless node.children

matched =
hook.args.empty? || if node.children.first.is_a?(::Parser::AST::Node)
node.children.any? do |child|
child.is_a?(::Parser::AST::Node) &&
match_children(hook.args[1..-1], child.children)
end
else
match_children(hook.args, node.children)
end

if matched
if hook.proc.arity == 1
hook.proc.call(node)
elsif hook.proc.arity == 2
walker = Walker.new(node)
hook.proc.call(node, walker)
walker.walk
end
end
end

def match_children(args, children)
args.each_with_index.all? do |arg, i|
if children[i].is_a?(::Parser::AST::Node)
children[i].type == arg
else
children[i] == arg
end
end
end
end
end
end
1 change: 1 addition & 0 deletions spec/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def initialize(folder)
end

def write_file(path, content)
FileUtils.mkdir_p(File.dirname(path))
File.write(path, content)
@files << path
end
Expand Down
59 changes: 59 additions & 0 deletions spec/solargraph-rails/schema_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,64 @@ class Account < ActiveRecord::Base
assert_public_instance_method(map, "Account#some_binary", ["String"])
assert_public_instance_method(map, "Account#some_timestamp", ["ActiveSupport::TimeWithZone"])
end

it 'infers prefixed table name' do
map = use_workspace "./spec/rails5" do |root|
root.write_file 'db/schema.rb', <<-RUBY
ActiveRecord::Schema.define(version: 2021_10_20_084658) do
create_table "accounting_invoices", force: :cascade do |t|
t.decimal "amount"
end
end
RUBY

root.write_file 'app/models/accounting/invoice.rb', <<-RUBY
class Accounting::Invoice < ActiveRecord::Base
end
RUBY
end

assert_public_instance_method(map, "Accounting::Invoice#amount", ["BigDecimal"])
end

it 'falls back unprefixed tables even if model is namespaced' do
map = use_workspace "./spec/rails5" do |root|
root.write_file 'db/schema.rb', <<-RUBY
ActiveRecord::Schema.define(version: 2021_10_20_084658) do
create_table "invoices", force: :cascade do |t|
t.decimal "amount"
end
end
RUBY

root.write_file 'app/models/accounting/invoice.rb', <<-RUBY
class Accounting::Invoice < ActiveRecord::Base
end
RUBY
end

# resolves to accounts table
assert_public_instance_method(map, "Accounting::Invoice#amount", ["BigDecimal"])
end

it 'uses explicit table name if defined' do
map = use_workspace "./spec/rails7" do |root|
root.write_file 'db/schema.rb', <<-RUBY
ActiveRecord::Schema.define(version: 2021_10_20_084658) do
create_table "bills", force: :cascade do |t|
t.decimal "amount"
end
end
RUBY

root.write_file 'app/models/invoice.rb', <<-RUBY
class Invoice < ActiveRecord::Base
self.table_name = 'bills'
end
RUBY
end

assert_public_instance_method(map, 'Invoice#amount', ['BigDecimal'])
end
end

1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'solargraph-rails'
require 'logger'
require 'byebug'
require 'fileutils'
require_relative './helpers'

RSpec.configure do |config|
Expand Down

0 comments on commit 1b322dd

Please sign in to comment.