Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ gem 'rbs', '>= 3'
gem 'rbs-inline', require: false
gem 'steep', '>= 1.4'
gem 'minitest'
gem 'minitest-hooks'
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ GEM
mini_mime (1.1.5)
mini_portile2 (2.8.9)
minitest (5.25.5)
minitest-hooks (1.5.2)
minitest (> 5.3)
mutex_m (0.3.0)
net-imap (0.5.7)
date
Expand Down Expand Up @@ -232,6 +234,7 @@ PLATFORMS

DEPENDENCIES
minitest
minitest-hooks
rails (>= 7.0)
rake (~> 13.0)
rbs (>= 3)
Expand Down
14 changes: 8 additions & 6 deletions lib/rbs_rails/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ def self.generatable?(klass) #: boolish

# @rbs klass: untyped
# @rbs dependencies: Array[String]
def self.class_to_rbs(klass, dependencies: []) #: untyped
Generator.new(klass, dependencies: dependencies).generate
def self.class_to_rbs(klass) #: untyped
Generator.new(klass).generate
end

class Generator
IGNORED_ENUM_KEYS = %i[_prefix _suffix _default _scopes] #: Array[Symbol]

# @rbs @parse_model_file: nil | Parser::AST::Node
# @rbs @dependencies: Array[String]
# @rbs @enum_definitions: Array[Hash[Symbol, untyped]]
# @rbs @klass_name: String

attr_reader :dependencies #: DependencyBuilder

# @rbs klass: singleton(ActiveRecord::Base) & Enum
# @rbs dependencies: Array[String]
def initialize(klass, dependencies:) #: untyped
def initialize(klass) #: untyped
@klass = klass
@dependencies = dependencies
@dependencies = DependencyBuilder.new
@klass_name = Util.module_name(klass, abs: false)

namespaces = klass_name(abs: false).split('::').tap{ |names| names.pop }
Expand Down Expand Up @@ -62,6 +62,8 @@ def generate #: String
#{collection_proxy_decl}

#{footer}

#{dependencies.build}
RBS
end

Expand Down
14 changes: 3 additions & 11 deletions lib/rbs_rails/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,16 @@ def install_hooks #: void
def generate_models #: void
Rails.application.eager_load!

dep_builder = DependencyBuilder.new

::ActiveRecord::Base.descendants.each do |klass|
generate_single_model(klass, dep_builder)
generate_single_model(klass)
rescue => e
puts "Error generating RBS for #{klass.name} model"
raise e
end

if dep_rbs = dep_builder.build
config.signature_root_dir.join('model_dependencies.rbs').write(dep_rbs)
end
end

# @rbs klass: singleton(ActiveRecord::Base)
# @rbs dep_builder: DependencyBuilder
def generate_single_model(klass, dep_builder) #: bool
def generate_single_model(klass) #: bool
return false if config.ignored_model?(klass)
return false unless RbsRails::ActiveRecord.generatable?(klass)

Expand All @@ -125,9 +118,8 @@ def generate_single_model(klass, dep_builder) #: bool
path = config.signature_root_dir / rbs_relative_path
path.dirname.mkpath

sig = RbsRails::ActiveRecord.class_to_rbs(klass, dependencies: dep_builder.deps)
sig = RbsRails::ActiveRecord.class_to_rbs(klass)
path.write sig
dep_builder.done << klass.name

true
end
Expand Down
18 changes: 17 additions & 1 deletion lib/rbs_rails/dependency_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,23 @@ class DependencyBuilder

def initialize #: void
@deps = []
@done = Set.new(['ActiveRecord::Base', 'ActiveRecord', 'Object'])
@done = Set.new([
'ActiveRecord',
'ActiveRecord::Associations',
'ActiveRecord::Associations::CollectionProxy',
'ActiveRecord::Base',
'ActiveRecord::Relation',
'ActiveStorage',
'ActiveStorage::Attachment',
'ActiveStorage::Blob',
'ActiveStorage::Record',
'Object'
])
end

# @rbs name: String
def <<(name) #: Array[String]
deps << name
end

def build #: String | nil
Expand Down
9 changes: 4 additions & 5 deletions sig/rbs_rails/active_record.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module RbsRails

# @rbs klass: untyped
# @rbs dependencies: Array[String]
def self.class_to_rbs: (untyped klass, ?dependencies: Array[String]) -> untyped
def self.class_to_rbs: (untyped klass) -> untyped

class Generator
IGNORED_ENUM_KEYS: Array[Symbol]
Expand All @@ -16,13 +16,12 @@ module RbsRails

@enum_definitions: Array[Hash[Symbol, untyped]]

@dependencies: Array[String]

@parse_model_file: nil | Parser::AST::Node

attr_reader dependencies: DependencyBuilder

# @rbs klass: singleton(ActiveRecord::Base) & Enum
# @rbs dependencies: Array[String]
def initialize: (singleton(ActiveRecord::Base) & Enum klass, dependencies: Array[String]) -> untyped
def initialize: (singleton(ActiveRecord::Base) & Enum klass) -> untyped

def generate: () -> String

Expand Down
3 changes: 1 addition & 2 deletions sig/rbs_rails/cli.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ module RbsRails
def generate_models: () -> void

# @rbs klass: singleton(ActiveRecord::Base)
# @rbs dep_builder: DependencyBuilder
def generate_single_model: (singleton(ActiveRecord::Base) klass, DependencyBuilder dep_builder) -> bool
def generate_single_model: (singleton(ActiveRecord::Base) klass) -> bool

def generate_path_helpers: () -> void

Expand Down
3 changes: 3 additions & 0 deletions sig/rbs_rails/dependency_builder.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ module RbsRails

def initialize: () -> void

# @rbs name: String
def <<: (String name) -> Array[String]

def build: () -> (String | nil)

private def shift: () -> (String | nil)
Expand Down
3 changes: 3 additions & 0 deletions test/expectations/audited_audit.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -681,3 +681,6 @@ module ::Audited
end
end
end

module ::Audited
end
7 changes: 7 additions & 0 deletions test/expectations/blog.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,10 @@ class ::Blog < ::ApplicationRecord
def prepend: (*::Blog | ::Array[::Blog]) -> self
end
end

class ::ApplicationRecord < ::ActiveRecord::Base
end
class ::User < ::ApplicationRecord
end
class ::User::ActiveRecord_Associations_CollectionProxy < ::ActiveRecord::Associations::CollectionProxy
end
7 changes: 7 additions & 0 deletions test/expectations/user.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,10 @@ class ::User < ::ApplicationRecord
def prepend: (*::User | ::Array[::User]) -> self
end
end

class ::ApplicationRecord < ::ActiveRecord::Base
end
class ::Blog < ::ApplicationRecord
end
class ::Blog::ActiveRecord_Associations_CollectionProxy < ::ActiveRecord::Associations::CollectionProxy
end
25 changes: 6 additions & 19 deletions test/rbs_rails/active_record_test.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
require 'test_helper'

class ActiveRecordTest < Minitest::Test
def test_type_check
include Minitest::Hooks

def before_all
clean_test_signatures

setup!
end

dir = app_dir
sh!('steep', 'check', chdir: dir)
def test_type_check
sh!('steep', 'check', chdir: app_dir)
end

def test_user_model_rbs_snapshot
clean_test_signatures

setup!

rbs_path = app_dir.join('sig/rbs_rails/app/models/user.rbs')
expect_path = expectations_dir / 'user.rbs'
# Code to re-generate the expectation files
Expand All @@ -24,10 +23,6 @@ def test_user_model_rbs_snapshot
end

def test_blog_model_rbs_snapshot
clean_test_signatures

setup!

rbs_path = app_dir.join('sig/rbs_rails/packs/blogs/app/models/blog.rbs')
expect_path = expectations_dir / 'blog.rbs'
# Code to re-generate the expectation files
Expand All @@ -37,19 +32,11 @@ def test_blog_model_rbs_snapshot
end

def test_article_model_rbs_is_skipped
clean_test_signatures

setup!

rbs_path = app_dir.join('sig/rbs_rails/app/models/article.rbs')
refute rbs_path.exist?
end

def test_external_library_model_rbs_generation
clean_test_signatures

setup!

rbs_path = app_dir.join('sig/rbs_rails/app/models/audited/audit.rbs')
expect_path = expectations_dir / 'audited_audit.rbs'
# Code to re-generate the expectation files
Expand Down
13 changes: 6 additions & 7 deletions test/rbs_rails/path_helper_test.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
require 'test_helper'

class PathHelperTest < Minitest::Test
def test_type_check
include Minitest::Hooks

def before_all
clean_test_signatures

setup!
end

dir = app_dir
sh!('steep', 'check', chdir: dir)
def test_type_check
sh!('steep', 'check', chdir: app_dir)
end

def test_user_model_rbs_snapshot
clean_test_signatures

setup!

rbs_path = app_dir.join('sig/rbs_rails/path_helpers.rbs')
expect_path = expectations_dir / 'path_helpers.rbs'
# Code to re-generate the expectation files
Expand Down
1 change: 1 addition & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'minitest'
require 'minitest/autorun'
require 'minitest/hooks'

require 'pathname'
require 'rbs_rails'
Expand Down