rbs_activerecord 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/.vscode/settings.json +2 -1
- data/README.md +34 -5
- data/lib/rbs_activerecord/generator.rb +21 -3
- data/lib/rbs_activerecord/parser/evaluator.rb +5 -0
- data/lib/rbs_activerecord/parser/include_expander/module.rb +47 -0
- data/lib/rbs_activerecord/parser/include_expander.rb +82 -0
- data/lib/rbs_activerecord/parser/visitor.rb +1 -16
- data/lib/rbs_activerecord/parser.rb +10 -4
- data/lib/rbs_activerecord/version.rb +1 -1
- data/lib/rbs_activerecord.rb +2 -0
- data/sig/rbs_activerecord/parser/include_expander/module.rbs +29 -0
- data/sig/rbs_activerecord/parser/include_expander.rbs +32 -0
- data/sig/rbs_activerecord/parser.rbs +7 -3
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec157eccf748c923a3f44138390f064e4b3aea347fdfe9744be6a5fa9954a947
|
4
|
+
data.tar.gz: f6a433a5a7c3dc60806045240bd71ee4817fc8c1cf36a0af37fc66098821ff36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8da50a2d1a78c22b997ab57c94476ee53abffb30688c4222982543dd84e9ed9dc97a99fd9a128a6fb4ba307f83d545e239291354ef67df0f66cfc7f4424b6d4c
|
7
|
+
data.tar.gz: 0fc67b6d496d3472a1f69ea3105d51da358d1ae3f48f1715bbcc24e02ffd229ff144319f493ecec23937f6b161ef00f11eaecba06e0b12fc49e480140bb9e1e8
|
data/.vscode/settings.json
CHANGED
data/README.md
CHANGED
@@ -6,23 +6,52 @@ rbs_activerecord is a RBSGenerator for models built with ActiveRecord.
|
|
6
6
|
|
7
7
|
Add a new entry to your Gemfile and run `bundle install`:
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
group :development do
|
10
|
+
gem 'rbs_activerecord', require: false
|
11
|
+
end
|
12
12
|
|
13
13
|
After the installation, please run rake task generator:
|
14
14
|
|
15
|
-
|
15
|
+
bundle exec rails g rbs_activerecord:install
|
16
16
|
|
17
17
|
## Usage
|
18
18
|
|
19
19
|
Run `rbs:activerecord:setup` task:
|
20
20
|
|
21
|
-
|
21
|
+
bundle exec rake rbs:activerecord:setup
|
22
22
|
|
23
23
|
Then rbs_activerecord will scan your code and generate RBS files into
|
24
24
|
`sig/activerecord` directory.
|
25
25
|
|
26
|
+
## The goal of rbs_activerecord
|
27
|
+
|
28
|
+
rbs_activerecord is an experimental project to enhance the type generation for ActiveRecord models.
|
29
|
+
It is strongly inspired by [rbs_rails](https://github.com/pocke/rbs_rails).
|
30
|
+
|
31
|
+
The goal of this gem is to give feedbacks to the rbs_rails project, and improving rbs_rails.
|
32
|
+
One of the large goals is merging this gem into rbs_rails.
|
33
|
+
|
34
|
+
Main differences between rbs_activerecord and rbs_rails are:
|
35
|
+
|
36
|
+
* New features
|
37
|
+
* Support Rails7 style enum definitions ([#268](https://github.com/pocke/rbs_rails/pull/268))
|
38
|
+
* Support STI (Single Table Inheritance) models ([#287](https://github.com/pocke/rbs_rails/pull/287))
|
39
|
+
* Inherits scope, enum and other definitions in the parent class to the child class
|
40
|
+
* Support has_and_belongs_to_many association ([#272](https://github.com/pocke/rbs_rails/pull/272))
|
41
|
+
* Support `alias_attribute` ([#263](https://github.com/pocke/rbs_rails/pull/263))
|
42
|
+
* Support generating model definitions within the concern module ([#209](https://github.com/pocke/rbs_rails/pull/209))
|
43
|
+
* Support extension ActiveRecord from 3rd party gems ([#254](https://github.com/pocke/rbs_rails/pull/254))
|
44
|
+
* ex. kaminari
|
45
|
+
* Support composite primary keys ([#275](https://github.com/pocke/rbs_rails/pull/275))
|
46
|
+
* Extend return types of #pluck ([#273](https://github.com/pocke/rbs_rails/pull/273))
|
47
|
+
* Override types of CollectionProxy ([#289](https://github.com/pocke/rbs_rails/pull/289))
|
48
|
+
* Bug fixes
|
49
|
+
* [#286](https://github.com/pocke/rbs_rails/pull/286)
|
50
|
+
* [#285](https://github.com/pocke/rbs_rails/pull/285)
|
51
|
+
* [#278](https://github.com/pocke/rbs_rails/pull/278)
|
52
|
+
* [#277](https://github.com/pocke/rbs_rails/pull/277)
|
53
|
+
* [#233](https://github.com/pocke/rbs_rails/pull/233)
|
54
|
+
|
26
55
|
## Development
|
27
56
|
|
28
57
|
After checking out the repo, run `bin/setup` to install dependencies. You can also
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "rails"
|
4
4
|
|
5
5
|
module RbsActiverecord
|
6
|
-
class Generator
|
6
|
+
class Generator # rubocop:disable Metrics/ClassLength
|
7
7
|
include Utils
|
8
8
|
|
9
9
|
attr_reader :klass #: singleton(ActiveRecord::Base)
|
@@ -17,7 +17,7 @@ module RbsActiverecord
|
|
17
17
|
@model = Model.new(klass)
|
18
18
|
end
|
19
19
|
|
20
|
-
def generate #: String # rubocop:disable Metrics/AbcSize
|
20
|
+
def generate #: String # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
21
21
|
format <<~RBS
|
22
22
|
#{header}
|
23
23
|
#{Attributes.new(model).generate}
|
@@ -34,6 +34,21 @@ module RbsActiverecord
|
|
34
34
|
#{Enum::Scopes.new(model, declarations).generate}
|
35
35
|
#{Scopes.new(model, declarations).generate}
|
36
36
|
|
37
|
+
module GeneratedCollectionProxyInstanceMethods[Model, PrimaryKey]
|
38
|
+
def build: (?ActiveRecord::Associations::CollectionProxy::_EachPair attributes) ?{ () -> untyped } -> Model
|
39
|
+
| (Array[ActiveRecord::Associations::CollectionProxy::_EachPair] attributes) ?{ () -> untyped } -> Array[Model]
|
40
|
+
def create: (?ActiveRecord::Associations::CollectionProxy::_EachPair attributes) ?{ () -> untyped } -> Model
|
41
|
+
| (Array[ActiveRecord::Associations::CollectionProxy::_EachPair] attributes) ?{ () -> untyped } -> Array[Model]
|
42
|
+
def create!: (?ActiveRecord::Associations::CollectionProxy::_EachPair attributes) ?{ () -> untyped } -> Model
|
43
|
+
| (Array[ActiveRecord::Associations::CollectionProxy::_EachPair] attributes) ?{ () -> untyped } -> Array[Model]
|
44
|
+
def reload: () -> Array[Model]
|
45
|
+
def replace: (Array[Model]) -> void
|
46
|
+
def delete: (*Model | PrimaryKey) -> Array[Model]
|
47
|
+
def destroy: (*Model | PrimaryKey) -> Array[Model]
|
48
|
+
def <<: (*Model | Array[Model]) -> self
|
49
|
+
def prepend: (*Model | Array[Model]) -> self
|
50
|
+
end
|
51
|
+
|
37
52
|
class ActiveRecord_Relation < ::ActiveRecord::Relation
|
38
53
|
include ::Enumerable[#{klass_name}]
|
39
54
|
include ::ActiveRecord::Relation::Methods[#{klass_name}, #{primary_key_type}]
|
@@ -46,6 +61,7 @@ module RbsActiverecord
|
|
46
61
|
include ::ActiveRecord::Relation::Methods[#{klass_name}, #{primary_key_type}]
|
47
62
|
#{relation_methods}
|
48
63
|
include GeneratedPluckOverloads
|
64
|
+
include GeneratedCollectionProxyInstanceMethods[#{klass_name}, #{primary_key_type}]
|
49
65
|
end
|
50
66
|
|
51
67
|
extend ::ActiveRecord::Base::ClassMethods[#{klass_name}, #{klass_name}::ActiveRecord_Relation, #{primary_key_type}]
|
@@ -74,7 +90,9 @@ module RbsActiverecord
|
|
74
90
|
@declarations ||= begin
|
75
91
|
filename = Rails.root.join(model.filename)
|
76
92
|
if filename.exist?
|
77
|
-
Parser.
|
93
|
+
Parser.parse_file(filename.to_s).tap do |decls|
|
94
|
+
Parser::IncludeExpander.expand(model, decls)
|
95
|
+
end
|
78
96
|
else
|
79
97
|
{}
|
80
98
|
end
|
@@ -31,6 +31,11 @@ module RbsActiverecord
|
|
31
31
|
[key, value]
|
32
32
|
end
|
33
33
|
end.to_h
|
34
|
+
when Prism::ConstantReadNode
|
35
|
+
node.name.to_s
|
36
|
+
when Prism::ConstantPathNode
|
37
|
+
parent = node.parent ? eval_node(node.parent) : nil
|
38
|
+
"#{parent}::#{node.name}"
|
34
39
|
when Prism::KeywordHashNode
|
35
40
|
node.elements.filter_map do |assoc|
|
36
41
|
case assoc
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RbsActiverecord
|
4
|
+
module Parser
|
5
|
+
class IncludeExpander
|
6
|
+
class Module
|
7
|
+
attr_reader :mod #: ::Module
|
8
|
+
attr_reader :name #: String
|
9
|
+
|
10
|
+
# @rbs mod: ::Module
|
11
|
+
# @rbs name: String
|
12
|
+
def initialize(mod, name) #: void
|
13
|
+
@mod = mod
|
14
|
+
@name = name
|
15
|
+
end
|
16
|
+
|
17
|
+
def concern? #: bool
|
18
|
+
mod.is_a?(::ActiveSupport::Concern)
|
19
|
+
end
|
20
|
+
|
21
|
+
def included_blocks #: Array[Prism::CallNode]
|
22
|
+
return [] unless concern?
|
23
|
+
|
24
|
+
declarations.select { |node| node.name == :included }
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# @rbs @declarations: Array[Prism::CallNode]
|
30
|
+
|
31
|
+
def declarations #: Array[Prism::CallNode]
|
32
|
+
@declarations ||= begin
|
33
|
+
path = source_location
|
34
|
+
return [] unless path && File.exist?(path)
|
35
|
+
|
36
|
+
declarations = Parser.parse_file(path)
|
37
|
+
declarations.fetch(name, [])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def source_location #: String?
|
42
|
+
Object.const_source_location(name)&.fetch(0)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RbsActiverecord
|
4
|
+
module Parser
|
5
|
+
class IncludeExpander
|
6
|
+
# @rbs model: RbsActiverecord::Model
|
7
|
+
# @rbs declarations: Hash[String, Array[Prism::CallNode]]
|
8
|
+
def self.expand(model, declarations) #: void
|
9
|
+
new(model, declarations).expand
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :model #: RbsActiverecord::Model
|
13
|
+
attr_reader :declarations #: Hash[String, Array[Prism::CallNode]]
|
14
|
+
|
15
|
+
# @rbs model: RbsActiverecord::Model
|
16
|
+
# @rbs declarations: Hash[String, Array[Prism::CallNode]]
|
17
|
+
def initialize(model, declarations) #: void
|
18
|
+
@model = model
|
19
|
+
@declarations = declarations
|
20
|
+
end
|
21
|
+
|
22
|
+
def expand #: void
|
23
|
+
declarations.each_value do |decls|
|
24
|
+
loop do
|
25
|
+
index = decls.index { |node| node.name == :include }
|
26
|
+
break unless index
|
27
|
+
|
28
|
+
included_module = decls.delete_at(index)
|
29
|
+
included_blocks = included_blocks_for(included_module)
|
30
|
+
included_blocks.reverse_each do |included|
|
31
|
+
decls.insert(index, *block_body_of(included))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# @rbs node: Prism::CallNode
|
40
|
+
def included_blocks_for(node) #: Array[Prism::CallNode]
|
41
|
+
modules = node.arguments&.arguments.to_a
|
42
|
+
.map { |arg| Parser.eval_node(arg) }
|
43
|
+
.select { |mod| mod.is_a?(String) }
|
44
|
+
modules.flat_map do |modname|
|
45
|
+
mod = const_get(modname)
|
46
|
+
next [] unless mod
|
47
|
+
|
48
|
+
Module.new(mod, modname).included_blocks
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @rbs name: String
|
53
|
+
def const_get(name) #: ::Module?
|
54
|
+
path = model.klass.name.to_s.split("::")
|
55
|
+
|
56
|
+
loop do
|
57
|
+
modname = (path + [name]).join("::")
|
58
|
+
return Object.const_get(modname) if Object.const_defined?(modname)
|
59
|
+
break if path.empty?
|
60
|
+
|
61
|
+
path.pop
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @rbs node: Prism::CallNode
|
66
|
+
def block_body_of(node) #: Array[Prism::CallNode]
|
67
|
+
case node.block
|
68
|
+
when Prism::BlockNode
|
69
|
+
case node.block.body
|
70
|
+
when Prism::StatementsNode
|
71
|
+
body = node.block.body.body #: Array[Prism::CallNode]
|
72
|
+
body.select { |n| n.is_a?(Prism::CallNode) }
|
73
|
+
else
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
else
|
77
|
+
[]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -14,22 +14,7 @@ module RbsActiverecord
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def current_namespace #: String
|
17
|
-
context.
|
18
|
-
namespace = [] #: Array[Symbol?]
|
19
|
-
path = node.constant_path #: Prism::Node?
|
20
|
-
loop do
|
21
|
-
case path
|
22
|
-
when Prism::ConstantPathNode
|
23
|
-
namespace << path.name
|
24
|
-
path = path.parent
|
25
|
-
when Prism::ConstantReadNode
|
26
|
-
namespace << path.name
|
27
|
-
break
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
namespace.compact.reverse
|
32
|
-
end.join("::")
|
17
|
+
context.map { |node| Parser.eval_node(node.constant_path) }.join("::")
|
33
18
|
end
|
34
19
|
|
35
20
|
# @rbs override
|
@@ -8,12 +8,13 @@ module RbsActiverecord
|
|
8
8
|
# refs: https://github.com/soutaro/rbs-inline/pull/118
|
9
9
|
#
|
10
10
|
# @rbs!
|
11
|
-
# def self.parse: (String
|
11
|
+
# def self.parse: (String code) -> Hash[String, Array[Prism::CallNode]]
|
12
|
+
# def self.parse_file: (String filename) -> Hash[String, Array[Prism::CallNode]]
|
12
13
|
# def self.eval_node: (Prism::Node node) -> untyped
|
13
14
|
|
14
|
-
# @rbs
|
15
|
-
def parse(
|
16
|
-
result = Prism.
|
15
|
+
# @rbs code: String
|
16
|
+
def parse(code) #: Hash[String, Array[Prism::CallNode]]
|
17
|
+
result = Prism.parse(code)
|
17
18
|
|
18
19
|
visitor = Visitor.new
|
19
20
|
visitor.visit(result.value)
|
@@ -21,6 +22,11 @@ module RbsActiverecord
|
|
21
22
|
end
|
22
23
|
module_function :parse
|
23
24
|
|
25
|
+
def parse_file(filename) #: Hash[String, Array[Prism::CallNode]]
|
26
|
+
File.open(filename) { |f| parse(f.read) }
|
27
|
+
end
|
28
|
+
module_function :parse_file
|
29
|
+
|
24
30
|
# @rbs node: Prism::Node
|
25
31
|
def eval_node(node) #: untyped
|
26
32
|
Evaluator.eval_node(node)
|
data/lib/rbs_activerecord.rb
CHANGED
@@ -18,6 +18,8 @@ require_relative "rbs_activerecord/generator/secure_password"
|
|
18
18
|
require_relative "rbs_activerecord/model"
|
19
19
|
require_relative "rbs_activerecord/parser"
|
20
20
|
require_relative "rbs_activerecord/parser/evaluator"
|
21
|
+
require_relative "rbs_activerecord/parser/include_expander"
|
22
|
+
require_relative "rbs_activerecord/parser/include_expander/module"
|
21
23
|
require_relative "rbs_activerecord/parser/visitor"
|
22
24
|
require_relative "rbs_activerecord/version"
|
23
25
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Generated from lib/rbs_activerecord/parser/include_expander/module.rb with RBS::Inline
|
2
|
+
|
3
|
+
module RbsActiverecord
|
4
|
+
module Parser
|
5
|
+
class IncludeExpander
|
6
|
+
class Module
|
7
|
+
attr_reader mod: ::Module
|
8
|
+
|
9
|
+
attr_reader name: String
|
10
|
+
|
11
|
+
# @rbs mod: ::Module
|
12
|
+
# @rbs name: String
|
13
|
+
def initialize: (::Module mod, String name) -> void
|
14
|
+
|
15
|
+
def concern?: () -> bool
|
16
|
+
|
17
|
+
def included_blocks: () -> Array[Prism::CallNode]
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
@declarations: Array[Prism::CallNode]
|
22
|
+
|
23
|
+
def declarations: () -> Array[Prism::CallNode]
|
24
|
+
|
25
|
+
def source_location: () -> String?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Generated from lib/rbs_activerecord/parser/include_expander.rb with RBS::Inline
|
2
|
+
|
3
|
+
module RbsActiverecord
|
4
|
+
module Parser
|
5
|
+
class IncludeExpander
|
6
|
+
# @rbs model: RbsActiverecord::Model
|
7
|
+
# @rbs declarations: Hash[String, Array[Prism::CallNode]]
|
8
|
+
def self.expand: (RbsActiverecord::Model model, Hash[String, Array[Prism::CallNode]] declarations) -> void
|
9
|
+
|
10
|
+
attr_reader model: RbsActiverecord::Model
|
11
|
+
|
12
|
+
attr_reader declarations: Hash[String, Array[Prism::CallNode]]
|
13
|
+
|
14
|
+
# @rbs model: RbsActiverecord::Model
|
15
|
+
# @rbs declarations: Hash[String, Array[Prism::CallNode]]
|
16
|
+
def initialize: (RbsActiverecord::Model model, Hash[String, Array[Prism::CallNode]] declarations) -> void
|
17
|
+
|
18
|
+
def expand: () -> void
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# @rbs node: Prism::CallNode
|
23
|
+
def included_blocks_for: (Prism::CallNode node) -> Array[Prism::CallNode]
|
24
|
+
|
25
|
+
# @rbs name: String
|
26
|
+
def const_get: (String name) -> ::Module?
|
27
|
+
|
28
|
+
# @rbs node: Prism::CallNode
|
29
|
+
def block_body_of: (Prism::CallNode node) -> Array[Prism::CallNode]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -2,12 +2,16 @@
|
|
2
2
|
|
3
3
|
module RbsActiverecord
|
4
4
|
module Parser
|
5
|
-
def self.parse: (String
|
5
|
+
def self.parse: (String code) -> Hash[String, Array[Prism::CallNode]]
|
6
|
+
|
7
|
+
def self.parse_file: (String filename) -> Hash[String, Array[Prism::CallNode]]
|
6
8
|
|
7
9
|
def self.eval_node: (Prism::Node node) -> untyped
|
8
10
|
|
9
|
-
# @rbs
|
10
|
-
def parse: (String
|
11
|
+
# @rbs code: String
|
12
|
+
def parse: (String code) -> Hash[String, Array[Prism::CallNode]]
|
13
|
+
|
14
|
+
def parse_file: (untyped filename) -> Hash[String, Array[Prism::CallNode]]
|
11
15
|
|
12
16
|
# @rbs node: Prism::Node
|
13
17
|
def eval_node: (Prism::Node node) -> untyped
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbs_activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takeshi KOMIYA
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -85,6 +85,8 @@ files:
|
|
85
85
|
- lib/rbs_activerecord/model.rb
|
86
86
|
- lib/rbs_activerecord/parser.rb
|
87
87
|
- lib/rbs_activerecord/parser/evaluator.rb
|
88
|
+
- lib/rbs_activerecord/parser/include_expander.rb
|
89
|
+
- lib/rbs_activerecord/parser/include_expander/module.rb
|
88
90
|
- lib/rbs_activerecord/parser/visitor.rb
|
89
91
|
- lib/rbs_activerecord/rake_task.rb
|
90
92
|
- lib/rbs_activerecord/utils.rb
|
@@ -109,6 +111,8 @@ files:
|
|
109
111
|
- sig/rbs_activerecord/model.rbs
|
110
112
|
- sig/rbs_activerecord/parser.rbs
|
111
113
|
- sig/rbs_activerecord/parser/evaluator.rbs
|
114
|
+
- sig/rbs_activerecord/parser/include_expander.rbs
|
115
|
+
- sig/rbs_activerecord/parser/include_expander/module.rbs
|
112
116
|
- sig/rbs_activerecord/parser/visitor.rbs
|
113
117
|
- sig/rbs_activerecord/rake_task.rbs
|
114
118
|
- sig/rbs_activerecord/utils.rbs
|