rbs_rails 0.8.1 → 0.10.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: 30c59f478f8fa0cd0c41751feb468a04208e057ff318edf80717b63d2f4664b8
4
- data.tar.gz: f382afdc82aaa47b6fe14cba2cdcbb62f127dc04d95020b71ed1b6c1aa8dfd9d
3
+ metadata.gz: 4daa0714f5e127bc21e33e9737983999459a2dbb690202c6dc86408a68ef82b0
4
+ data.tar.gz: 54f9a8c04ca0f22c1a391a4d5a47c0294c54f50741843226655ae9e8175b0a51
5
5
  SHA512:
6
- metadata.gz: 54ca5ed84a59c4e67892f8c4e72cdc190420d457345523eb11b3dd4efe7dde5d8662afc7ace9dbee3be1aa5577b257b028ccdba20297b45332919999ac648914
7
- data.tar.gz: 61ba620c335f27ddb58ce96d4bf3d03ea61071c528b587b7fa419d93c462ba6d96e6ad4e508243a1e6aff903966bfa49e18a02ecf9eb69f053c2cdd215da20a7
6
+ metadata.gz: f85bdbf2f59f5accdd54a062f5d5412f432b9d91b62b6f4ced371fbcc0d565ad661a1015d9193e4ee9add64ef69cf7243dd5603dc22b05182b59f3a122ecad2e
7
+ data.tar.gz: f75cc735df8961b0714af8bf4f38771f01542ed4162e69fb8adc677dcbfec0885fe15296bc84006b955d9370004dfcd6070fbd761c0b220f02d16f062fadcbba
@@ -0,0 +1,5 @@
1
+ version: 1
2
+ update_configs:
3
+ - package_manager: "ruby:bundler"
4
+ directory: "/"
5
+ update_schedule: "live"
@@ -15,7 +15,7 @@ jobs:
15
15
  strategy:
16
16
  fail-fast: false
17
17
  matrix:
18
- ruby: [2.6, 2.7, '3.0', head]
18
+ ruby: [2.6, 2.7, '3.0', 3.1, head]
19
19
  runs-on: ubuntu-latest
20
20
  steps:
21
21
  - uses: actions/checkout@v2
@@ -23,7 +23,5 @@ jobs:
23
23
  with:
24
24
  ruby-version: ${{ matrix.ruby }}
25
25
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
26
- - env:
27
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28
- run: bin/gem_rbs
26
+ - run: bin/setup
29
27
  - run: bundle exec rake
data/.gitignore CHANGED
@@ -6,8 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
- /Gemfile.lock
10
9
  /test/app/sig/rbs_rails
11
10
  /test/app/sig/app
12
11
  /test/app/sig/path_helpers.rbs
13
- /gem_rbs
12
+ /.gem_rbs_collection/
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  ## master (unreleased)
4
4
 
5
+ ## 0.9.0 (2021-09-18)
6
+
7
+ ### New Features
8
+
9
+ * Support delegated_type association. [#181](https://github.com/pocke/rbs_rails/pull/181)
10
+
11
+ ### Bug Fixes
12
+
13
+ * Fix error on a table that doesn't have the PK. [#121](https://github.com/pocke/rbs_rails/pull/121)
14
+ * Quote variable names to avoid syntax errors. [#178](https://github.com/pocke/rbs_rails/pull/178)
15
+ * Add scope methods to `ActiveRecord_Associations_CollectionProxy`. [#182](https://github.com/pocke/rbs_rails/pull/182)
16
+ * Make `has_one` association optional. [#180](https://github.com/pocke/rbs_rails/pull/180)
17
+ * Omit some methods for polymorphic associations. [#184](https://github.com/pocke/rbs_rails/pull/184)
18
+ * Include enum methods to GeneratedRelationMethods. [#183](https://github.com/pocke/rbs_rails/pull/183)
19
+ * Include `_ActiveRecord_Relation` to `CollectionProxy`. [#189](https://github.com/pocke/rbs_rails/pull/189)
20
+
21
+ ## 0.8.2 (2021-02-20)
22
+
23
+ * Add ActiveRecord::AttributeMethods::Dirty methods [#104](https://github.com/pocke/rbs_rails/pull/104)
24
+ * Define find method based on primary key [#105](https://github.com/pocke/rbs_rails/pull/105)
25
+
5
26
  ## 0.8.1 (2021-01-09)
6
27
 
7
28
  * Skip generation RBS fro class that doesn't have table in DB. [#95](https://github.com/pocke/rbs_rails/pull/95)
data/Gemfile CHANGED
@@ -1,9 +1,12 @@
1
1
  source "https://rubygems.org"
2
2
 
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
3
5
  # Specify your gem's dependencies in rbs_rails.gemspec
4
6
  gemspec
5
7
 
6
- gem "rake", "~> 12.0"
7
- gem 'rbs', '1.0.0'
8
- gem 'steep', '>= 0.34.0', git: 'https://github.com/soutaro/steep.git'
8
+ gem "rake", "~> 13.0"
9
+ gem 'rails', '< 6.1', '>= 6.0'
10
+ gem 'rbs', '>= 2'
11
+ gem 'steep'
9
12
  gem 'minitest'
data/Gemfile.lock ADDED
@@ -0,0 +1,174 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rbs_rails (0.10.0)
5
+ parser
6
+ rbs (>= 1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (6.0.4.7)
12
+ actionpack (= 6.0.4.7)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailbox (6.0.4.7)
16
+ actionpack (= 6.0.4.7)
17
+ activejob (= 6.0.4.7)
18
+ activerecord (= 6.0.4.7)
19
+ activestorage (= 6.0.4.7)
20
+ activesupport (= 6.0.4.7)
21
+ mail (>= 2.7.1)
22
+ actionmailer (6.0.4.7)
23
+ actionpack (= 6.0.4.7)
24
+ actionview (= 6.0.4.7)
25
+ activejob (= 6.0.4.7)
26
+ mail (~> 2.5, >= 2.5.4)
27
+ rails-dom-testing (~> 2.0)
28
+ actionpack (6.0.4.7)
29
+ actionview (= 6.0.4.7)
30
+ activesupport (= 6.0.4.7)
31
+ rack (~> 2.0, >= 2.0.8)
32
+ rack-test (>= 0.6.3)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
35
+ actiontext (6.0.4.7)
36
+ actionpack (= 6.0.4.7)
37
+ activerecord (= 6.0.4.7)
38
+ activestorage (= 6.0.4.7)
39
+ activesupport (= 6.0.4.7)
40
+ nokogiri (>= 1.8.5)
41
+ actionview (6.0.4.7)
42
+ activesupport (= 6.0.4.7)
43
+ builder (~> 3.1)
44
+ erubi (~> 1.4)
45
+ rails-dom-testing (~> 2.0)
46
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
47
+ activejob (6.0.4.7)
48
+ activesupport (= 6.0.4.7)
49
+ globalid (>= 0.3.6)
50
+ activemodel (6.0.4.7)
51
+ activesupport (= 6.0.4.7)
52
+ activerecord (6.0.4.7)
53
+ activemodel (= 6.0.4.7)
54
+ activesupport (= 6.0.4.7)
55
+ activestorage (6.0.4.7)
56
+ actionpack (= 6.0.4.7)
57
+ activejob (= 6.0.4.7)
58
+ activerecord (= 6.0.4.7)
59
+ marcel (~> 1.0.0)
60
+ activesupport (6.0.4.7)
61
+ concurrent-ruby (~> 1.0, >= 1.0.2)
62
+ i18n (>= 0.7, < 2)
63
+ minitest (~> 5.1)
64
+ tzinfo (~> 1.1)
65
+ zeitwerk (~> 2.2, >= 2.2.2)
66
+ ast (2.4.2)
67
+ builder (3.2.4)
68
+ concurrent-ruby (1.1.9)
69
+ crass (1.0.6)
70
+ erubi (1.10.0)
71
+ ffi (1.15.5)
72
+ globalid (1.0.0)
73
+ activesupport (>= 5.0)
74
+ i18n (1.10.0)
75
+ concurrent-ruby (~> 1.0)
76
+ language_server-protocol (3.16.0.3)
77
+ listen (3.7.1)
78
+ rb-fsevent (~> 0.10, >= 0.10.3)
79
+ rb-inotify (~> 0.9, >= 0.9.10)
80
+ loofah (2.15.0)
81
+ crass (~> 1.0.2)
82
+ nokogiri (>= 1.5.9)
83
+ mail (2.7.1)
84
+ mini_mime (>= 0.1.1)
85
+ marcel (1.0.2)
86
+ method_source (1.0.0)
87
+ mini_mime (1.1.2)
88
+ mini_portile2 (2.8.0)
89
+ minitest (5.15.0)
90
+ nio4r (2.5.8)
91
+ nokogiri (1.13.3)
92
+ mini_portile2 (~> 2.8.0)
93
+ racc (~> 1.4)
94
+ parallel (1.21.0)
95
+ parser (3.1.1.0)
96
+ ast (~> 2.4.1)
97
+ racc (1.6.0)
98
+ rack (2.2.3)
99
+ rack-test (1.1.0)
100
+ rack (>= 1.0, < 3)
101
+ rails (6.0.4.7)
102
+ actioncable (= 6.0.4.7)
103
+ actionmailbox (= 6.0.4.7)
104
+ actionmailer (= 6.0.4.7)
105
+ actionpack (= 6.0.4.7)
106
+ actiontext (= 6.0.4.7)
107
+ actionview (= 6.0.4.7)
108
+ activejob (= 6.0.4.7)
109
+ activemodel (= 6.0.4.7)
110
+ activerecord (= 6.0.4.7)
111
+ activestorage (= 6.0.4.7)
112
+ activesupport (= 6.0.4.7)
113
+ bundler (>= 1.3.0)
114
+ railties (= 6.0.4.7)
115
+ sprockets-rails (>= 2.0.0)
116
+ rails-dom-testing (2.0.3)
117
+ activesupport (>= 4.2.0)
118
+ nokogiri (>= 1.6)
119
+ rails-html-sanitizer (1.4.2)
120
+ loofah (~> 2.3)
121
+ railties (6.0.4.7)
122
+ actionpack (= 6.0.4.7)
123
+ activesupport (= 6.0.4.7)
124
+ method_source
125
+ rake (>= 0.8.7)
126
+ thor (>= 0.20.3, < 2.0)
127
+ rainbow (3.1.1)
128
+ rake (13.0.6)
129
+ rb-fsevent (0.11.1)
130
+ rb-inotify (0.10.1)
131
+ ffi (~> 1.0)
132
+ rbs (2.2.2)
133
+ sprockets (4.0.3)
134
+ concurrent-ruby (~> 1.0)
135
+ rack (> 1, < 3)
136
+ sprockets-rails (3.4.2)
137
+ actionpack (>= 5.2)
138
+ activesupport (>= 5.2)
139
+ sprockets (>= 3.0.0)
140
+ steep (0.49.1)
141
+ activesupport (>= 5.1)
142
+ language_server-protocol (>= 3.15, < 4.0)
143
+ listen (~> 3.0)
144
+ parallel (>= 1.0.0)
145
+ parser (>= 3.0)
146
+ rainbow (>= 2.2.2, < 4.0)
147
+ rbs (>= 2.2.0)
148
+ terminal-table (>= 2, < 4)
149
+ terminal-table (3.0.2)
150
+ unicode-display_width (>= 1.1.1, < 3)
151
+ thor (1.2.1)
152
+ thread_safe (0.3.6)
153
+ tzinfo (1.2.9)
154
+ thread_safe (~> 0.1)
155
+ unicode-display_width (2.1.0)
156
+ websocket-driver (0.7.5)
157
+ websocket-extensions (>= 0.1.0)
158
+ websocket-extensions (0.1.5)
159
+ zeitwerk (2.5.4)
160
+
161
+ PLATFORMS
162
+ ruby
163
+ x86_64-linux
164
+
165
+ DEPENDENCIES
166
+ minitest
167
+ rails (>= 6.0, < 6.1)
168
+ rake (~> 13.0)
169
+ rbs (>= 2)
170
+ rbs_rails!
171
+ steep
172
+
173
+ BUNDLED WITH
174
+ 2.2.27
data/README.md CHANGED
@@ -58,6 +58,9 @@ target :app do
58
58
  library 'monitor'
59
59
  library 'singleton'
60
60
  library 'tsort'
61
+ library 'time'
62
+
63
+ library 'rack'
61
64
 
62
65
  library 'activesupport'
63
66
  library 'actionpack'
@@ -69,12 +72,11 @@ target :app do
69
72
  end
70
73
  ```
71
74
 
72
- You need to put RBS repo to `path/to/rbs_repo`. See https://github.com/ruby/gem_rbs
75
+ You need to put RBS repo to `path/to/rbs_repo`. See https://github.com/ruby/gem_rbs_collection
73
76
 
74
77
  ## Development
75
78
 
76
- After checking out the repo, run `GITHUB_TOKEN=xxx bin/setup` to install dependencies.
77
- `GITHUB_TOKEN` environment variable is required to fetch RBS from [ruby/gem_rbs](https://github.com/ruby/gem_rbs) repository.
79
+ After checking out the repo, run `bin/setup` to install dependencies.
78
80
 
79
81
  You can also run `bin/console` for an interactive prompt that will allow you to experiment.
80
82
 
data/Rakefile CHANGED
@@ -9,8 +9,10 @@ task :steep do
9
9
  end
10
10
 
11
11
  task :rbs_validate do
12
- rbs = Pathname(__dir__).join('bin/rbs').to_s
13
- sh "ruby #{rbs} validate --silent"
12
+ repo = ENV['RBS_REPO_DIR']&.then do |env|
13
+ "--repo=#{env}"
14
+ end
15
+ sh "rbs #{repo} validate --silent"
14
16
  end
15
17
 
16
18
  Rake::TestTask.new do |test|
data/Steepfile CHANGED
@@ -3,21 +3,5 @@ target :lib do
3
3
  signature 'assets/sig'
4
4
 
5
5
  check "lib" # Directory name
6
- repo_path ENV['RBS_REPO_DIR'] || './gem_rbs/gems'
7
-
8
- library "pathname"
9
- library "logger"
10
- library "mutex_m"
11
- library "date"
12
- library 'monitor'
13
- library 'singleton'
14
- library 'tsort'
15
-
16
- library 'activesupport'
17
- library 'actionpack'
18
- library 'activejob'
19
- library 'activemodel'
20
- library 'actionview'
21
- library 'activerecord'
22
- library 'railties'
6
+ repo_path ENV['RBS_REPO_DIR'] if ENV['RBS_REPO_DIR']
23
7
  end
data/bin/setup CHANGED
@@ -4,6 +4,6 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle install
7
- bin/gem_rbs
7
+ bundle exec rbs collection install --frozen
8
8
 
9
9
  # Do any other automated setup that you need to do here
@@ -28,14 +28,20 @@ module RbsRails
28
28
  private def klass_decl
29
29
  <<~RBS
30
30
  #{header}
31
- extend _ActiveRecord_Relation_ClassMethods[#{klass_name}, #{relation_class_name}]
31
+ extend _ActiveRecord_Relation_ClassMethods[#{klass_name}, #{relation_class_name}, #{pk_type}]
32
32
 
33
33
  #{columns}
34
34
  #{associations}
35
+ #{generated_association_methods}
36
+ #{has_secure_password}
37
+ #{delegated_type_instance}
38
+ #{delegated_type_scope(singleton: true)}
35
39
  #{enum_instance_methods}
36
40
  #{enum_scope_methods(singleton: true)}
37
41
  #{scopes(singleton: true)}
38
42
 
43
+ #{generated_relation_methods_decl}
44
+
39
45
  #{relation_decl}
40
46
 
41
47
  #{collection_proxy_decl}
@@ -44,20 +50,39 @@ module RbsRails
44
50
  RBS
45
51
  end
46
52
 
53
+ private def pk_type
54
+ pk = klass.primary_key
55
+ return 'top' unless pk
56
+
57
+ col = klass.columns.find {|col| col.name == pk }
58
+ sql_type_to_class(col.type)
59
+ end
60
+
61
+ private def generated_relation_methods_decl
62
+ <<~RBS
63
+ module GeneratedRelationMethods
64
+ #{enum_scope_methods(singleton: false)}
65
+ #{scopes(singleton: false)}
66
+ #{delegated_type_scope(singleton: false)}
67
+ end
68
+ RBS
69
+ end
70
+
47
71
  private def relation_decl
48
72
  <<~RBS
49
- class #{relation_class_name} < ActiveRecord::Relation
50
- include _ActiveRecord_Relation[#{klass_name}]
73
+ class #{relation_class_name} < ::ActiveRecord::Relation
74
+ include GeneratedRelationMethods
75
+ include _ActiveRecord_Relation[#{klass_name}, #{pk_type}]
51
76
  include Enumerable[#{klass_name}]
52
- #{enum_scope_methods(singleton: false)}
53
- #{scopes(singleton: false)}
54
77
  end
55
78
  RBS
56
79
  end
57
80
 
58
81
  private def collection_proxy_decl
59
82
  <<~RBS
60
- class ActiveRecord_Associations_CollectionProxy < ActiveRecord::Associations::CollectionProxy
83
+ class ActiveRecord_Associations_CollectionProxy < ::ActiveRecord::Associations::CollectionProxy
84
+ include GeneratedRelationMethods
85
+ include _ActiveRecord_Relation[#{klass_name}, #{pk_type}]
61
86
  end
62
87
  RBS
63
88
  end
@@ -74,7 +99,7 @@ module RbsRails
74
99
  superclass_name = Util.module_name(superclass)
75
100
  @dependencies << superclass_name
76
101
 
77
- "class #{mod_name} < #{superclass_name}"
102
+ "class #{mod_name} < ::#{superclass_name}"
78
103
  when Module
79
104
  "module #{mod_name}"
80
105
  else
@@ -114,7 +139,7 @@ module RbsRails
114
139
  type = a.polymorphic? ? 'untyped' : Util.module_name(a.klass)
115
140
  type_optional = optional(type)
116
141
  <<~RUBY.chomp
117
- def #{a.name}: () -> #{type}
142
+ def #{a.name}: () -> #{type_optional}
118
143
  def #{a.name}=: (#{type_optional}) -> #{type_optional}
119
144
  def build_#{a.name}: (untyped) -> #{type}
120
145
  def create_#{a.name}: (untyped) -> #{type}
@@ -128,23 +153,162 @@ module RbsRails
128
153
  klass.reflect_on_all_associations(:belongs_to).map do |a|
129
154
  type = a.polymorphic? ? 'untyped' : Util.module_name(a.klass)
130
155
  type_optional = optional(type)
131
- <<~RUBY.chomp
132
- def #{a.name}: () -> #{type}
133
- def #{a.name}=: (#{type_optional}) -> #{type_optional}
134
- def build_#{a.name}: (untyped) -> #{type}
135
- def create_#{a.name}: (untyped) -> #{type}
136
- def create_#{a.name}!: (untyped) -> #{type}
137
- def reload_#{a.name}: () -> #{type_optional}
138
- RUBY
156
+ # @type var methods: Array[String]
157
+ methods = []
158
+ methods << "def #{a.name}: () -> #{type}"
159
+ methods << "def #{a.name}=: (#{type_optional}) -> #{type_optional}"
160
+ methods << "def reload_#{a.name}: () -> #{type_optional}"
161
+ if !a.polymorphic?
162
+ methods << "def build_#{a.name}: (untyped) -> #{type}"
163
+ methods << "def create_#{a.name}: (untyped) -> #{type}"
164
+ methods << "def create_#{a.name}!: (untyped) -> #{type}"
165
+ end
166
+ methods.join("\n")
139
167
  end.join("\n")
140
168
  end
141
169
 
170
+ private def generated_association_methods
171
+ # @type var sigs: Array[String]
172
+ sigs = []
173
+
174
+ # Needs to require "active_storage/engine"
175
+ if klass.respond_to?(:attachment_reflections)
176
+ sigs << "module GeneratedAssociationMethods"
177
+ sigs << klass.attachment_reflections.map do |name, reflection|
178
+ case reflection.macro
179
+ when :has_one_attached
180
+ <<~EOS
181
+ def #{name}: () -> ActiveStorage::Attached::One
182
+ def #{name}=: (ActionDispatch::Http::UploadedFile) -> ActionDispatch::Http::UploadedFile
183
+ | (Rack::Test::UploadedFile) -> Rack::Test::UploadedFile
184
+ | (ActiveStorage::Blob) -> ActiveStorage::Blob
185
+ | (String) -> String
186
+ | ({ io: IO, filename: String, content_type: String? }) -> { io: IO, filename: String, content_type: String? }
187
+ | (nil) -> nil
188
+ EOS
189
+ when :has_many_attached
190
+ <<~EOS
191
+ def #{name}: () -> ActiveStorage::Attached::Many
192
+ def #{name}=: (untyped) -> untyped
193
+ EOS
194
+ else
195
+ raise
196
+ end
197
+ end.join("\n")
198
+ sigs << "end"
199
+ sigs << "include GeneratedAssociationMethods"
200
+ end
201
+
202
+ sigs.join("\n")
203
+ end
204
+
205
+ private def delegated_type_scope(singleton:)
206
+ definitions = delegated_type_definitions
207
+ return "" unless definitions
208
+ definitions.map do |definition|
209
+ definition[:types].map do |type|
210
+ scope_name = type.tableize.gsub("/", "_")
211
+ "def #{singleton ? 'self.' : ''}#{scope_name}: () -> #{relation_class_name}"
212
+ end
213
+ end.flatten.join("\n")
214
+ end
215
+
216
+ private def delegated_type_instance
217
+ definitions = delegated_type_definitions
218
+ return "" unless definitions
219
+ # @type var methods: Array[String]
220
+ methods = []
221
+ definitions.each do |definition|
222
+ methods << "def #{definition[:role]}_class: () -> Class"
223
+ methods << "def #{definition[:role]}_name: () -> String"
224
+ methods << definition[:types].map do |type|
225
+ scope_name = type.tableize.gsub("/", "_")
226
+ singular = scope_name.singularize
227
+ <<~RUBY.chomp
228
+ def #{singular}?: () -> bool
229
+ def #{singular}: () -> #{type.classify}?
230
+ def #{singular}_id: () -> Integer?
231
+ RUBY
232
+ end.join("\n")
233
+ end
234
+ methods.join("\n")
235
+ end
236
+
237
+ private def delegated_type_definitions
238
+ ast = parse_model_file
239
+ return unless ast
240
+
241
+ traverse(ast).map do |node|
242
+ # @type block: { role: Symbol, types: Array[String] }?
243
+ next unless node.type == :send
244
+ next unless node.children[0].nil?
245
+ next unless node.children[1] == :delegated_type
246
+
247
+ role_node = node.children[2]
248
+ next unless role_node
249
+ next unless role_node.type == :sym
250
+ # @type var role: Symbol
251
+ role = role_node.children[0]
252
+
253
+ args_node = node.children[3]
254
+ next unless args_node
255
+ next unless args_node.type == :hash
256
+
257
+ types = traverse(args_node).map do |n|
258
+ # @type block: Array[String]?
259
+ next unless n.type == :pair
260
+ key_node = n.children[0]
261
+ next unless key_node
262
+ next unless key_node.type == :sym
263
+ next unless key_node.children[0] == :types
264
+
265
+ types_node = n.children[1]
266
+ next unless types_node
267
+ next unless types_node.type == :array
268
+ code = types_node.loc.expression.source
269
+ eval(code)
270
+ end.compact.flatten
271
+
272
+ { role: role, types: types }
273
+ end.compact
274
+ end
275
+
276
+ private def has_secure_password
277
+ ast = parse_model_file
278
+ return unless ast
279
+
280
+ traverse(ast).map do |node|
281
+ # @type block: String?
282
+ next unless node.type == :send
283
+ next unless node.children[0].nil?
284
+ next unless node.children[1] == :has_secure_password
285
+
286
+ attribute_node = node.children[2]
287
+ attribute = if attribute_node && attribute_node.type == :sym
288
+ attribute_node.children[0]
289
+ else
290
+ :password
291
+ end
292
+
293
+ <<~EOS
294
+ module ActiveModel_SecurePassword_InstanceMethodsOnActivation_#{attribute}
295
+ attr_reader #{attribute}: String?
296
+ def #{attribute}=: (String) -> String
297
+ def #{attribute}_confirmation=: (String) -> String
298
+ def authenticate_#{attribute}: (String) -> (#{klass_name} | false)
299
+ #{attribute == :password ? "alias authenticate authenticate_password" : ""}
300
+ end
301
+ include ActiveModel_SecurePassword_InstanceMethodsOnActivation_#{attribute}
302
+ EOS
303
+ end.compact.join("\n")
304
+ end
305
+
142
306
  private def enum_instance_methods
143
307
  # @type var methods: Array[String]
144
308
  methods = []
145
309
  enum_definitions.each do |hash|
146
310
  hash.each do |name, values|
147
- next if name == :_prefix || name == :_suffix
311
+ next if name == :_prefix || name == :_suffix || name == :_default
148
312
 
149
313
  values.each do |label, value|
150
314
  value_method_name = enum_method_name(hash, name, label)
@@ -162,7 +326,7 @@ module RbsRails
162
326
  methods = []
163
327
  enum_definitions.each do |hash|
164
328
  hash.each do |name, values|
165
- next if name == :_prefix || name == :_suffix
329
+ next if name == :_prefix || name == :_suffix || name == :_default
166
330
 
167
331
  values.each do |label, value|
168
332
  value_method_name = enum_method_name(hash, name, label)
@@ -223,7 +387,9 @@ module RbsRails
223
387
  ast = parse_model_file
224
388
  return '' unless ast
225
389
 
226
- traverse(ast).map do |node|
390
+ prefix = singleton ? 'self.' : ''
391
+
392
+ sigs = traverse(ast).map do |node|
227
393
  # @type block: nil | String
228
394
  next unless node.type == :send
229
395
  next unless node.children[0].nil?
@@ -239,8 +405,16 @@ module RbsRails
239
405
  next unless body_node.type == :block
240
406
 
241
407
  args = args_to_type(body_node.children[1])
242
- "def #{singleton ? 'self.' : ''}#{name}: #{args} -> #{relation_class_name}"
243
- end.compact.join("\n")
408
+ "def #{prefix}#{name}: #{args} -> #{relation_class_name}"
409
+ end.compact
410
+
411
+ if klass.respond_to?(:attachment_reflections)
412
+ klass.attachment_reflections.each do |name, _reflection|
413
+ sigs << "def #{prefix}with_attached_#{name}: () -> #{relation_class_name}"
414
+ end
415
+ end
416
+
417
+ sigs.join("\n")
244
418
  end
245
419
 
246
420
  private def args_to_type(args_node)
@@ -251,17 +425,17 @@ module RbsRails
251
425
  args_node.children.each do |node|
252
426
  case node.type
253
427
  when :arg
254
- res << "untyped #{node.children[0]}"
428
+ res << "untyped `#{node.children[0]}`"
255
429
  when :optarg
256
- res << "?untyped #{node.children[0]}"
430
+ res << "?untyped `#{node.children[0]}`"
257
431
  when :kwarg
258
432
  res << "#{node.children[0]}: untyped"
259
433
  when :kwoptarg
260
434
  res << "?#{node.children[0]}: untyped"
261
435
  when :restarg
262
- res << "*untyped #{node.children[0]}"
436
+ res << "*untyped `#{node.children[0]}`"
263
437
  when :kwrestarg
264
- res << "**untyped #{node.children[0]}"
438
+ res << "**untyped `#{node.children[0]}`"
265
439
  when :blockarg
266
440
  block = " { (*untyped) -> untyped }"
267
441
  else
@@ -285,7 +459,7 @@ module RbsRails
285
459
  end
286
460
 
287
461
  private def traverse(node, &block)
288
- return to_enum(__method__, node) unless block_given?
462
+ return to_enum(__method__ || raise, node) unless block_given?
289
463
 
290
464
  # @type var block: ^(Parser::AST::Node) -> untyped
291
465
  block.call node
@@ -299,7 +473,8 @@ module RbsRails
299
473
  end
300
474
 
301
475
  private def columns
302
- klass.columns.map do |col|
476
+ mod_sig = +"module GeneratedAttributeMethods\n"
477
+ mod_sig << klass.columns.map do |col|
303
478
  class_name = if enum_definitions.any? { |hash| hash.key?(col.name) || hash.key?(col.name.to_sym) }
304
479
  'String'
305
480
  else
@@ -308,7 +483,9 @@ module RbsRails
308
483
  class_name_opt = optional(class_name)
309
484
  column_type = col.null ? class_name_opt : class_name
310
485
  sig = <<~EOS
311
- attr_accessor #{col.name} (): #{column_type}
486
+ def #{col.name}: () -> #{column_type}
487
+ def #{col.name}=: (#{column_type}) -> #{column_type}
488
+ def #{col.name}?: () -> bool
312
489
  def #{col.name}_changed?: () -> bool
313
490
  def #{col.name}_change: () -> [#{class_name_opt}, #{class_name_opt}]
314
491
  def #{col.name}_will_change!: () -> void
@@ -316,12 +493,21 @@ module RbsRails
316
493
  def #{col.name}_previously_changed?: () -> bool
317
494
  def #{col.name}_previous_change: () -> Array[#{class_name_opt}]?
318
495
  def #{col.name}_previously_was: () -> #{class_name_opt}
496
+ def #{col.name}_before_last_save: () -> #{class_name_opt}
497
+ def #{col.name}_change_to_be_saved: () -> Array[#{class_name_opt}]?
498
+ def #{col.name}_in_database: () -> #{class_name_opt}
499
+ def saved_change_to_#{col.name}: () -> Array[#{class_name_opt}]?
500
+ def saved_change_to_#{col.name}?: () -> bool
501
+ def will_save_change_to_#{col.name}?: () -> bool
319
502
  def restore_#{col.name}!: () -> void
320
503
  def clear_#{col.name}_change: () -> void
321
504
  EOS
322
- sig << "attr_accessor #{col.name}? (): #{class_name}\n" if col.type == :boolean
505
+ sig << "\n"
323
506
  sig
324
507
  end.join("\n")
508
+ mod_sig << "\nend\n"
509
+ mod_sig << "include GeneratedAttributeMethods"
510
+ mod_sig
325
511
  end
326
512
 
327
513
  private def optional(class_name)
@@ -2,5 +2,5 @@ module RbsRails
2
2
  # Because of copy_signatures is defined by lib/rbs_rails.rb
3
3
  # @dynamic self.copy_signatures
4
4
 
5
- VERSION = "0.8.1"
5
+ VERSION = "0.10.0"
6
6
  end
@@ -0,0 +1,144 @@
1
+ ---
2
+ sources:
3
+ - name: ruby/gem_rbs_collection
4
+ remote: https://github.com/ruby/gem_rbs_collection.git
5
+ revision: main
6
+ repo_dir: gems
7
+ path: ".gem_rbs_collection"
8
+ gems:
9
+ - name: logger
10
+ version: '0'
11
+ source:
12
+ type: stdlib
13
+ - name: set
14
+ version: '0'
15
+ source:
16
+ type: stdlib
17
+ - name: pathname
18
+ version: '0'
19
+ source:
20
+ type: stdlib
21
+ - name: json
22
+ version: '0'
23
+ source:
24
+ type: stdlib
25
+ - name: optparse
26
+ version: '0'
27
+ source:
28
+ type: stdlib
29
+ - name: rubygems
30
+ version: '0'
31
+ source:
32
+ type: stdlib
33
+ - name: tsort
34
+ version: '0'
35
+ source:
36
+ type: stdlib
37
+ - name: actionpack
38
+ version: '6.0'
39
+ source:
40
+ type: git
41
+ name: ruby/gem_rbs_collection
42
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
43
+ remote: https://github.com/ruby/gem_rbs_collection.git
44
+ repo_dir: gems
45
+ - name: actionview
46
+ version: '6.0'
47
+ source:
48
+ type: git
49
+ name: ruby/gem_rbs_collection
50
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
51
+ remote: https://github.com/ruby/gem_rbs_collection.git
52
+ repo_dir: gems
53
+ - name: activejob
54
+ version: '6.0'
55
+ source:
56
+ type: git
57
+ name: ruby/gem_rbs_collection
58
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
59
+ remote: https://github.com/ruby/gem_rbs_collection.git
60
+ repo_dir: gems
61
+ - name: activemodel
62
+ version: '6.0'
63
+ source:
64
+ type: git
65
+ name: ruby/gem_rbs_collection
66
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
67
+ remote: https://github.com/ruby/gem_rbs_collection.git
68
+ repo_dir: gems
69
+ - name: activerecord
70
+ version: '6.0'
71
+ source:
72
+ type: git
73
+ name: ruby/gem_rbs_collection
74
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
75
+ remote: https://github.com/ruby/gem_rbs_collection.git
76
+ repo_dir: gems
77
+ - name: activestorage
78
+ version: '6.0'
79
+ source:
80
+ type: git
81
+ name: ruby/gem_rbs_collection
82
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
83
+ remote: https://github.com/ruby/gem_rbs_collection.git
84
+ repo_dir: gems
85
+ - name: activesupport
86
+ version: '6.0'
87
+ source:
88
+ type: git
89
+ name: ruby/gem_rbs_collection
90
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
91
+ remote: https://github.com/ruby/gem_rbs_collection.git
92
+ repo_dir: gems
93
+ - name: parallel
94
+ version: '1.20'
95
+ source:
96
+ type: git
97
+ name: ruby/gem_rbs_collection
98
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
99
+ remote: https://github.com/ruby/gem_rbs_collection.git
100
+ repo_dir: gems
101
+ - name: rack
102
+ version: 2.2.2
103
+ source:
104
+ type: git
105
+ name: ruby/gem_rbs_collection
106
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
107
+ remote: https://github.com/ruby/gem_rbs_collection.git
108
+ repo_dir: gems
109
+ - name: railties
110
+ version: '6.0'
111
+ source:
112
+ type: git
113
+ name: ruby/gem_rbs_collection
114
+ revision: 6320a194c85afbf4ff332c3eb75fcae2a6518c92
115
+ remote: https://github.com/ruby/gem_rbs_collection.git
116
+ repo_dir: gems
117
+ - name: rbs
118
+ version: 2.2.2
119
+ source:
120
+ type: rubygems
121
+ - name: monitor
122
+ version: '0'
123
+ source:
124
+ type: stdlib
125
+ - name: tempfile
126
+ version: '0'
127
+ source:
128
+ type: stdlib
129
+ - name: date
130
+ version: '0'
131
+ source:
132
+ type: stdlib
133
+ - name: singleton
134
+ version: '0'
135
+ source:
136
+ type: stdlib
137
+ - name: mutex_m
138
+ version: '0'
139
+ source:
140
+ type: stdlib
141
+ - name: time
142
+ version: '0'
143
+ source:
144
+ type: stdlib
@@ -0,0 +1,36 @@
1
+ # Download sources
2
+ sources:
3
+ - name: ruby/gem_rbs_collection
4
+ remote: https://github.com/ruby/gem_rbs_collection.git
5
+ revision: main
6
+ repo_dir: gems
7
+
8
+ # A directory to install the downloaded RBSs
9
+ path: .gem_rbs_collection
10
+
11
+ gems:
12
+ # Ignores - Gemfile.lock contains them but their RBSs are unnecessary
13
+ - name: steep
14
+ ignore: true
15
+ - name: nokogiri
16
+ ignore: true
17
+ - name: ast
18
+ ignore: true
19
+ - name: rainbow
20
+ ignore: true
21
+ - name: listen
22
+ ignore: true
23
+
24
+ # ignore RBS Rails itself
25
+ - name: rbs_rails
26
+ ignore: true
27
+
28
+ # They'are necessary to load RBS gem.
29
+ # See https://github.com/ruby/rbs/pull/921
30
+ - name: logger
31
+ - name: set
32
+ - name: pathname
33
+ - name: json
34
+ - name: optparse
35
+ - name: rubygems
36
+ - name: tsort
@@ -0,0 +1,4 @@
1
+ # TODO: Define it in gem_rbs_collection repository
2
+ class ActiveRecord::Base
3
+ def self.primary_key: () -> String
4
+ end
@@ -12,6 +12,10 @@ class RbsRails::ActiveRecord::Generator
12
12
 
13
13
  def klass_decl: () -> String
14
14
 
15
+ def pk_type: () -> String
16
+
17
+ def generated_relation_methods_decl: () -> String
18
+
15
19
  def relation_decl: () -> String
16
20
 
17
21
  def collection_proxy_decl: () -> String
@@ -28,6 +32,16 @@ class RbsRails::ActiveRecord::Generator
28
32
 
29
33
  def belongs_to: () -> String
30
34
 
35
+ def generated_association_methods: () -> String
36
+
37
+ def delegated_type_scope: (singleton: bool) -> String
38
+
39
+ def delegated_type_instance: () -> String
40
+
41
+ def delegated_type_definitions: () -> Array[{ role: Symbol, types: Array[String] }]?
42
+
43
+ def has_secure_password: () -> String?
44
+
31
45
  def enum_instance_methods: () -> String
32
46
 
33
47
  def enum_scope_methods: (singleton: untyped `singleton`) -> String
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbs_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masataka Pocke Kuwabara
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-08 00:00:00.000000000 Z
11
+ date: 2022-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -45,20 +45,20 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - ".dependabot/config.yml"
48
49
  - ".github/workflows/ci.yml"
49
50
  - ".gitignore"
50
51
  - ".gitmodules"
51
52
  - CHANGELOG.md
52
53
  - Gemfile
54
+ - Gemfile.lock
53
55
  - LICENSE
54
56
  - README.md
55
57
  - Rakefile
56
58
  - Steepfile
57
59
  - bin/add-type-params.rb
58
60
  - bin/console
59
- - bin/gem_rbs
60
61
  - bin/postprocess.rb
61
- - bin/rbs
62
62
  - bin/rbs-prototype-rb.rb
63
63
  - bin/setup
64
64
  - bin/to-ascii.rb
@@ -69,7 +69,10 @@ files:
69
69
  - lib/rbs_rails/rake_task.rb
70
70
  - lib/rbs_rails/util.rb
71
71
  - lib/rbs_rails/version.rb
72
+ - rbs_collection.lock.yaml
73
+ - rbs_collection.yaml
72
74
  - rbs_rails.gemspec
75
+ - sig/activerecord.rbs
73
76
  - sig/fileutils.rbs
74
77
  - sig/parser.rbs
75
78
  - sig/rake.rbs
@@ -102,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
105
  - !ruby/object:Gem::Version
103
106
  version: '0'
104
107
  requirements: []
105
- rubygems_version: 3.3.0.dev
108
+ rubygems_version: 3.4.0.dev
106
109
  signing_key:
107
110
  specification_version: 4
108
111
  summary: A RBS files generator for Rails application
data/bin/gem_rbs DELETED
@@ -1,94 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'net/http'
4
- require 'json'
5
- require 'securerandom'
6
- require 'pathname'
7
-
8
- TOKEN = ENV.fetch('GITHUB_TOKEN')
9
- VERSION = "6.0.3.2"
10
-
11
- def req(query)
12
- http = Net::HTTP.new("api.github.com", 443)
13
- http.use_ssl = true
14
- header = {
15
- "Authorization" => "Bearer #{TOKEN}",
16
- 'Content-Type' => 'application/json',
17
- 'User-Agent' => 'gem_rbs client',
18
- }
19
- resp = http.request_post('/graphql', JSON.generate(query), header)
20
- JSON.parse(resp.body, symbolize_names: true).tap do |content|
21
- raise content[:errors].inspect if content[:errors]
22
- end
23
- end
24
-
25
- class QueryBuilder
26
- attr_reader :variables
27
-
28
- def initialize
29
- @queries = []
30
- @variables = {}
31
- end
32
-
33
- def add(query, variables)
34
- query = query.dup
35
- variables = variables.transform_keys do |key|
36
- next key unless @variables.key?(key)
37
-
38
- new_key = key + '_' + SecureRandom.hex(8)
39
- query.gsub!(key, new_key)
40
- new_key
41
- end
42
-
43
- @queries << query
44
- @variables.merge!(variables)
45
- end
46
-
47
- def query
48
- # TODO: Allow non-String type for variables
49
- "query(#{variables.keys.map { |v| "$#{v}: String!" }.join(',')}) { #{@queries.join("\n")} }"
50
- end
51
- end
52
-
53
- gems = %w[activesupport actionpack activejob activemodel actionview activerecord railties]
54
-
55
- builder = QueryBuilder.new
56
- gems.each do |gem|
57
- path = "main:gems/#{gem}/#{VERSION}"
58
- builder.add(<<~GRAPHQL, { 'path' => path })
59
- #{gem}:repository(owner: "ruby", name: "gem_rbs") {
60
- object(expression: $path) {
61
- ... on Tree {
62
- entries {
63
- name
64
- object {
65
- ... on Blob {
66
- isTruncated
67
- text
68
- }
69
- }
70
- }
71
- }
72
- }
73
- }
74
- GRAPHQL
75
- end
76
-
77
- resp = req(query: builder.query, variables: builder.variables)
78
-
79
- resp[:data].each do |gem_name, gem_value|
80
- gem_value.dig(:object, :entries).each do |entry|
81
- fname = entry[:name]
82
- if fname.end_with?('.rbs')
83
- content =
84
- if entry.dig(:object, :isTruncated)
85
- `curl -H 'Accept: application/vnd.github.v3.raw' -H Authorization: token #{TOKEN} https://api.github.com/repos/ruby/gem_rbs/contents/gems/#{gem_name}/#{VERSION}/#{fname}`
86
- else
87
- entry.dig(:object, :text)
88
- end
89
- dir = Pathname("gem_rbs/gems/#{gem_name}/#{VERSION}")
90
- dir.mkpath
91
- dir.join(fname).write(content)
92
- end
93
- end
94
- end
data/bin/rbs DELETED
@@ -1,30 +0,0 @@
1
- #!ruby
2
-
3
- require 'pathname'
4
- root = Pathname(__dir__) / '../'
5
-
6
- def v(require)
7
- if v = ENV['RAILS_VERSION']
8
- "#{require}:#{v}"
9
- else
10
- require
11
- end
12
- end
13
-
14
- def repo
15
- ENV['RBS_REPO_DIR'] || Pathname(__dir__).join('../gem_rbs/gems').to_s
16
- end
17
-
18
- exec(
19
- 'rbs',
20
- # Require stdlibs
21
- '-rlogger', '-rpathname', '-rmutex_m', '-rdate', '-rmonitor', '-rsingleton', '-rtsort',
22
- "--repo=#{repo}",
23
- # Require Rails libraries
24
- v('-ractivesupport'), v('-ractionpack'), v('-ractivejob'), v('-ractivemodel'), v('-ractionview'), v('-ractiverecord'), v('-rrailties'),
25
- # Load signatures that are bundled in rbs_rails
26
- '-I' + root.join('sig').to_s, '-I' + root.join('assets/sig').to_s,
27
- # Expand arguments
28
- *ARGV,
29
- )
30
-