rbs_activerecord 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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