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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4083cdfd1907ddd9ba845bf3535ed264504753c6fe80c08ac723891a0c990aa
4
- data.tar.gz: 8825d942ba46a8b35cd4aa3d57ef723aa7fbc51333d7fe30bdca1b143ed73693
3
+ metadata.gz: ec157eccf748c923a3f44138390f064e4b3aea347fdfe9744be6a5fa9954a947
4
+ data.tar.gz: f6a433a5a7c3dc60806045240bd71ee4817fc8c1cf36a0af37fc66098821ff36
5
5
  SHA512:
6
- metadata.gz: '07838fda26b2d56872fa91f7d3d9a89b2eb89c5c93591722547ca27af65d65ff72ae1703c8366d4c556559d18cbcc8668930c8c4e2bbb7c89aaaa3e1b7ec84a3'
7
- data.tar.gz: 605694bc6458aa4f6076d89add3a162c66edceefd357f2402e989a093f898b853f1d363cf7d121a4e5e49befa4747bd99c2ae9a52f2a89502f25361ee1865ccf
6
+ metadata.gz: 8da50a2d1a78c22b997ab57c94476ee53abffb30688c4222982543dd84e9ed9dc97a99fd9a128a6fb4ba307f83d545e239291354ef67df0f66cfc7f4424b6d4c
7
+ data.tar.gz: 0fc67b6d496d3472a1f69ea3105d51da358d1ae3f48f1715bbcc24e02ffd229ff144319f493ecec23937f6b161ef00f11eaecba06e0b12fc49e480140bb9e1e8
@@ -7,6 +7,7 @@
7
7
  "cSpell.words": [
8
8
  "activerecord",
9
9
  "codebases",
10
- "Cyclomatic"
10
+ "Cyclomatic",
11
+ "kaminari"
11
12
  ]
12
13
  }
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
- group :development do
10
- gem 'rbs_activerecord', require: false
11
- end
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
- bundle exec rails g rbs_activerecord:install
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
- bundle exec rake rbs:activerecord:setup
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.parse(filename.to_s)
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.flat_map do |node|
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 filename) -> Hash[String, Array[Prism::CallNode]]
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 filename: String
15
- def parse(filename) #: Hash[String, Array[Prism::CallNode]]
16
- result = Prism.parse_file(filename)
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RbsActiverecord
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -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 filename) -> Hash[String, Array[Prism::CallNode]]
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 filename: String
10
- def parse: (String filename) -> Hash[String, Array[Prism::CallNode]]
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.0.0
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-14 00:00:00.000000000 Z
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