sord 7.0.0 → 7.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: 0cd5e0c9f0db7151e0f04f309f277fecb64ce6aaf2fefdff88f749ce7a6d02d1
4
- data.tar.gz: 8ef9d26ef5b4e6cf6acc7a6bfa9f46887a07891346e37cdb46b5e48d903c22bd
3
+ metadata.gz: 8f9681f7b1837fb0fb51588fb60d8bb94406b9bb9a488670244542971b0b66c7
4
+ data.tar.gz: 7db1dc6e7a1e3cf43d08ef6850da15eaebf18463fb5a329cdf42acb7da12c0a0
5
5
  SHA512:
6
- metadata.gz: 5b5dbaa497fe039afcb125a9e7801bdb124ed1e06b2ba0970b3743f9cfad1aa92a982710fa0f2eb5da9647803e8b2e8d6d11e3b236891687eca35c4f16c25626
7
- data.tar.gz: 3b432c0005c9cc1d00b6ec4c42a52eb8be6e8dc8425e6eb2a168b1c503e6f6df5fa5ea2a2cfb4c3e4fad98de88104e2166f8cda469305ec383719080a7836ffe
6
+ metadata.gz: 84ae70452bf558690f4da7c7fb6c2b1ba65b4cf906a813b04c27766b7ad7ddb549013b49f149593c0a4366016689c0f21f904c09cd3533a19f42ea814655fea7
7
+ data.tar.gz: 349e397a495df5dd01d7a13eac6541f9d2b0446dbc150e74be756dbc02ead11e7c558ec27f111d852f6c39f4719deb6ba6c56bffed99a44af5a11570f20ea3fc
@@ -0,0 +1,22 @@
1
+ name: Run Sord on examples
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ continue-on-error: false
9
+ steps:
10
+ - uses: actions/checkout@v2
11
+ - name: Set up Ruby
12
+ uses: ruby/setup-ruby@v1
13
+ with:
14
+ ruby-version: 3.4 # Use latest for best chance of dependencies resolving
15
+ - name: Install dependencies
16
+ run: bundle install
17
+ - name: Install example dependencies
18
+ run: sudo apt-get install libldap2-dev libidn11-dev # Dependencies for `addressable`
19
+ - name: Run examples (RBI)
20
+ run: bundle exec rake examples:seed[rbi]
21
+ - name: Run examples (RBS)
22
+ run: bundle exec rake examples:reseed[rbs]
@@ -6,7 +6,7 @@ jobs:
6
6
  test:
7
7
  strategy:
8
8
  matrix:
9
- ruby: [3.0, 3.1]
9
+ ruby: [3.0, 3.1, 3.2, 3.3, 3.4]
10
10
  continue-on-error: false
11
11
 
12
12
  runs-on: ubuntu-latest
data/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
+ ## [7.1.0] - 2025-07-07
7
+ ### Added
8
+ - `nil` can be used as an alias for `NilClass` in more places in YARD types. (Thanks @apiology)
9
+ - The Solargraph convention `undefined` can now be used as an "untyped" type. (Thanks @apiology)
10
+
11
+ ### Changed
12
+ - If a constant's value does not parse successfully, type information can still be generated for
13
+ that constant, and Sord emits a warning. Previously, this would cause a fatal exception.
14
+ - The version restriction on the RBS gem has been relaxed, to permit usage of 4.x versions.
15
+ RBS 3.x can still be used as before.
16
+ (Thanks @apiology)
17
+
18
+ ### Fixed
19
+ - Sord now correctly resolves namespaces when classes have a namespace, e.g. `class X::Y`.
20
+ (Thanks @dorner)
21
+
6
22
  ## [7.0.0] - 2025-03-03
7
23
  ### Added
8
24
  - Messages now show the file and line number that the message originated from. (Thanks @apiology)
data/Rakefile CHANGED
@@ -19,85 +19,138 @@ REPOS = {
19
19
  zeitwerk: 'https://github.com/fxn/zeitwerk'
20
20
  }
21
21
 
22
- namespace :examples do
23
- require 'fileutils'
24
- require 'rainbow'
22
+ # Thrown by tasks to present a "friendly" error message and abort the task.
23
+ class TaskError < StandardError
24
+ end
25
25
 
26
- desc "Clone git repositories and run Sord on them as examples"
27
- task :seed, [:mode, :clean] do |t, args|
28
- if File.directory?('sord_examples')
29
- puts Rainbow('sord_examples directory already exists, please delete the directory or run a reseed!').red
30
- exit
31
- end
26
+ # Handles Sord examples, including checkout and running Sord to generate types.
27
+ class ExampleRunner
28
+ attr_reader :mode, :mode_arg, :clean
29
+ alias clean? clean
30
+
31
+ # @return [<Symbol>] Names of gems where Sord failed.
32
+ attr_accessor :failed_examples
32
33
 
33
- if args[:mode] == 'rbi'
34
- mode_arg = '--rbi'
35
- elsif args[:mode] == 'rbs'
36
- mode_arg = '--rbs'
34
+ # @param [String] mode "rbi" or "rbs".
35
+ # @param [Boolean] clean Run Sord with additional options to generate minimal type output.
36
+ def initialize(mode:, clean: false)
37
+ @mode = mode
38
+ @clean = clean
39
+
40
+ if mode == 'rbi'
41
+ @mode_arg = '--rbi'
42
+ elsif mode == 'rbs'
43
+ @mode_arg = '--rbs'
37
44
  else
38
- puts Rainbow('please specify \'rbi\' or \'rbs\'!').red
39
- exit
45
+ raise TaskError, 'please specify \'rbi\' or \'rbs\'!'
46
+ end
47
+
48
+ @failed_examples = []
49
+ end
50
+
51
+ # Create the `sord_examples` directory, ready for checkouts.
52
+ # @raise [TaskError] If it already exists.
53
+ def create_examples_dir
54
+ if File.directory?(File.join(__dir__, 'sord_examples'))
55
+ raise TaskError, 'sord_examples directory already exists, please delete the directory or run a reseed!'
40
56
  end
41
57
 
42
58
  FileUtils.mkdir 'sord_examples'
43
- FileUtils.cd 'sord_examples'
44
-
45
- Bundler.with_clean_env do
46
- # Shallow clone each of the repositories, then bundle install and run sord.
47
- REPOS.each do |name, url|
48
- puts "Cloning #{name}..."
49
- system("git clone #{url} --depth=1")
50
- FileUtils.cd name.to_s
59
+ end
60
+
61
+ # Check that the `sord_examples` directory exists.
62
+ # @raise [TaskError] If it doesn't.
63
+ def validate_examples_dir
64
+ unless File.directory?(File.join(__dir__, 'sord_examples'))
65
+ raise TaskError, 'The sord_examples directory does not exist. Have you run the seed task?'
66
+ end
67
+ end
68
+
69
+ # Check out a repository, add Sord to its Gemfile, and install its dependencies.
70
+ # @param [Symbol] name Name of the checkout.
71
+ # @param [String] url Git URL.
72
+ def prepare_checkout(name, url)
73
+ Dir.chdir(File.join(__dir__, 'sord_examples')) do
74
+ puts "Cloning #{name}..."
75
+ system("git clone #{url} --depth=1")
76
+
77
+ Dir.chdir(name.to_s) do
51
78
  # Add sord to gemfile.
52
79
  `echo "gem 'sord', path: '../../'" >> Gemfile`
80
+
53
81
  # Run bundle install.
54
82
  system('bundle install')
55
- # Generate sri
56
- puts "Generating rbi for #{name}..."
57
- if args[:clean]
58
- system("bundle exec sord ../#{name}.#{args[:mode]} #{mode_arg} --no-sord-comments --replace-errors-with-untyped --replace-unresolved-with-untyped")
59
- else
60
- system("bundle exec sord ../#{name}.#{args[:mode]} #{mode_arg}")
61
- end
62
- puts "#{name}.#{args[:mode]} generated!"
63
- FileUtils.cd '..'
64
83
  end
65
84
  end
85
+ end
66
86
 
67
- puts Rainbow("Seeding complete!").green
87
+ # Run Sord on a checked-out repository.
88
+ # @param [Symbol] name Name of the checkout.
89
+ def generate_types(name)
90
+ puts "Generating rbi for #{name}..."
91
+
92
+ Dir.chdir(File.join(__dir__, 'sord_examples', name.to_s)) do
93
+ if clean?
94
+ system("bundle exec sord ../#{name}.#{mode} #{mode_arg} --no-sord-comments --replace-errors-with-untyped --replace-unresolved-with-untyped")
95
+ else
96
+ system("bundle exec sord ../#{name}.#{mode} #{mode_arg}")
97
+ end
98
+
99
+ if $?.success?
100
+ puts "#{name}.#{mode} generated!"
101
+ else
102
+ puts "Sord exited with error"
103
+ failed_examples << name
104
+ end
105
+ end
68
106
  end
69
107
 
70
- desc 'Regenerate the rbi files in sord_examples.'
71
- task :reseed, [:mode, :clean] do |t, args|
72
- if Dir.exist?('sord_examples')
73
- FileUtils.cd 'sord_examples'
74
- else
75
- raise Rainbow("The sord_examples directory does not exist. Have you run the seed task?").red.bold
108
+ # Check that all Sord executions were successful.
109
+ # @raise [TaskError] If any failed.
110
+ def validate_sord_success
111
+ if failed_examples.any?
112
+ raise TaskError, "Not all Sord runs were successful: #{failed_examples.map(&:to_s).join(', ')}"
76
113
  end
114
+ end
115
+ end
77
116
 
78
- if args[:mode] == 'rbi'
79
- mode_arg = '--rbi'
80
- elsif args[:mode] == 'rbs'
81
- mode_arg = '--rbs'
82
- else
83
- puts Rainbow('please specify \'rbi\' or \'rbs\'!').red
84
- exit
117
+ namespace :examples do
118
+ require 'fileutils'
119
+ require 'rainbow'
120
+
121
+ desc "Clone git repositories and run Sord on them as examples"
122
+ task :seed, [:mode, :clean] do |t, args|
123
+ examples = ExampleRunner.new(**args)
124
+ examples.create_examples_dir
125
+
126
+ Bundler.with_clean_env do
127
+ REPOS.each do |name, url|
128
+ examples.prepare_checkout(name, url)
129
+ examples.generate_types(name)
130
+ end
85
131
  end
132
+ examples.validate_sord_success
133
+
134
+ puts Rainbow("Seeding complete!").green
135
+
136
+ rescue TaskError => e
137
+ abort Rainbow(e.to_s).red
138
+ end
139
+
140
+ desc 'Regenerate the rbi files in sord_examples.'
141
+ task :reseed, [:mode, :clean] do |t, args|
142
+ examples = ExampleRunner.new(**args)
143
+ examples.validate_examples_dir
86
144
 
87
145
  REPOS.keys.each do |name|
88
- FileUtils.cd name.to_s
89
- puts "Regenerating rbi file for #{name}..."
90
- Bundler.with_clean_env do
91
- if args[:clean]
92
- system("bundle exec sord ../#{name}.#{args[:mode]} #{mode_arg} --no-regenerate --no-sord-comments --replace-errors-with-untyped --replace-unresolved-with-untyped")
93
- else
94
- system("bundle exec sord ../#{name}.#{args[:mode]} #{mode_arg} --no-regenerate")
95
- end
96
- end
97
- FileUtils.cd '..'
146
+ examples.generate_types(name)
98
147
  end
148
+ examples.validate_sord_success
99
149
 
100
150
  puts Rainbow("Re-seeding complete!").green
151
+
152
+ rescue TaskError => e
153
+ abort Rainbow(e.to_s).red
101
154
  end
102
155
 
103
156
  desc 'Delete the sord_examples directory to allow the seeder to run again.'
@@ -143,22 +143,29 @@ module Sord
143
143
  when :rbi
144
144
  # Parse so we can set up constant with correct heredoc syntax
145
145
  kwargs = {}
146
- value_node = Parser::CurrentRuby.parse(constant.value)
147
- loc = value_node.loc
148
- if loc.instance_of? Parser::Source::Map::Heredoc
149
- #
150
- # heredocs in Ruby come after the full expression is complete. e.g.,
151
- # puts(>>FOO)
152
- # bar
153
- # FOO
154
- #
155
- # so if we want to wrap them in a T.let, we need to parse out the expression vs the rest
156
- kwargs[:heredocs] = constant.value[loc.heredoc_body.begin_pos...loc.heredoc_end.end_pos]
157
- expression = loc.expression
158
- value = constant.value[expression.begin_pos...expression.end_pos]
159
- else
160
- value = constant.value
146
+
147
+ value = constant.value
148
+ begin
149
+ value_node = Parser::CurrentRuby.parse(constant.value)
150
+ loc = value_node.loc
151
+ if loc.instance_of? Parser::Source::Map::Heredoc
152
+ #
153
+ # heredocs in Ruby come after the full expression is complete. e.g.,
154
+ # puts(>>FOO)
155
+ # bar
156
+ # FOO
157
+ #
158
+ # so if we want to wrap them in a T.let, we need to parse out the expression vs the rest
159
+ kwargs[:heredocs] = constant.value[loc.heredoc_body.begin_pos...loc.heredoc_end.end_pos]
160
+ expression = loc.expression
161
+ value = constant.value[expression.begin_pos...expression.end_pos]
162
+ end
163
+
164
+ rescue Parser::SyntaxError => e
165
+ # Emit a warning, since this may cause a syntax error when parsing the RBI
166
+ Logging.warn("syntax error on constant value: #{e}", constant)
161
167
  end
168
+
162
169
  @current_object.create_constant(constant_name, value: "T.let(#{value}, T.untyped)", **kwargs) do |c|
163
170
  c.add_comments(constant.docstring.all.split("\n"))
164
171
  end
@@ -681,10 +688,8 @@ module Sord
681
688
  end
682
689
  end
683
690
  rescue
684
- Logging.error($!)
685
- $@.each do |line|
686
- puts " #{line}"
687
- end
691
+ Logging.error("An internal error occurred while running Sord: #{$!}")
692
+ raise
688
693
  end
689
694
 
690
695
  # Given two pairs of arrays representing method parameters, in the form
data/lib/sord/resolver.rb CHANGED
@@ -70,14 +70,21 @@ module Sord
70
70
  klasses = [
71
71
  Parlour::RbiGenerator::Constant,
72
72
  Parlour::RbiGenerator::ModuleNamespace,
73
- Parlour::RbiGenerator::ClassNamespace
73
+ Parlour::RbiGenerator::ClassNamespace,
74
+ Parlour::RbiGenerator::Namespace
75
+ ]
76
+ child_only_classes = [
77
+ Parlour::RbiGenerator::Namespace
74
78
  ]
75
79
  nodes.each do |node|
76
- next unless klasses.include?(node.class)
77
- new_path = path + [node.name]
78
- names_to_paths[node.name] ||= Set.new
79
- names_to_paths[node.name] << new_path.join('::')
80
- add_rbi_objects_to_paths(node.children, names_to_paths, new_path) if node.respond_to?(:children)
80
+ if klasses.include?(node.class)
81
+ new_path = path + [node.name]
82
+ unless child_only_classes.include?(node.class)
83
+ names_to_paths[node.name] ||= Set.new
84
+ names_to_paths[node.name] << new_path.join('::')
85
+ end
86
+ add_rbi_objects_to_paths(node.children, names_to_paths, new_path) if node.respond_to?(:children)
87
+ end
81
88
  end
82
89
  end
83
90
 
@@ -129,8 +129,12 @@ module Sord
129
129
  case yard
130
130
  when nil # Type not specified
131
131
  Parlour::Types::Untyped.new
132
+ when "nil"
133
+ Parlour::Types::Raw.new('NilClass')
132
134
  when "bool", "Bool", "boolean", "Boolean", "true", "false"
133
135
  Parlour::Types::Boolean.new
136
+ when "undefined" # solargraph convention
137
+ Parlour::Types::Untyped.new
134
138
  when 'self'
135
139
  Parlour::Types::Self.new
136
140
  when Array
@@ -195,10 +199,11 @@ module Sord
195
199
  relative_generic_type = generic_type.start_with?('::') \
196
200
  ? generic_type[2..-1] : generic_type
197
201
 
198
- parameters = split_type_parameters(type_parameters)
202
+ yard_parameters = split_type_parameters(type_parameters)
203
+ parameters = yard_parameters
199
204
  .map { |x| yard_to_parlour(x, item, config) }
200
- if SINGLE_ARG_GENERIC_TYPES.include?(relative_generic_type) && parameters.length > 1
201
- Parlour::Types.const_get(relative_generic_type).new(Parlour::Types::Union.new(parameters))
205
+ if SINGLE_ARG_GENERIC_TYPES.include?(relative_generic_type) && yard_parameters.length > 1
206
+ Parlour::Types.const_get(relative_generic_type).new(yard_to_parlour(yard_parameters, item, config))
202
207
  elsif relative_generic_type == 'Class'
203
208
  if parameters.length == 1
204
209
  Parlour::Types::Class.new(parameters.first)
data/lib/sord/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # typed: strong
2
2
  module Sord
3
- VERSION = '7.0.0'
3
+ VERSION = '7.1.0'
4
4
  end
data/rbi/sord.rbi CHANGED
@@ -1,7 +1,7 @@
1
1
  # typed: strong
2
2
  # typed: strong
3
3
  module Sord
4
- VERSION = T.let('7.0.0', T.untyped)
4
+ VERSION = T.let('7.1.0', T.untyped)
5
5
 
6
6
  # Handles writing logs to stdout and any other classes which request them.
7
7
  module Logging
data/sord.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency 'commander', '~> 5.0'
28
28
  spec.add_dependency "parser"
29
29
  spec.add_dependency 'parlour', '~> 9.1'
30
- spec.add_dependency 'rbs', '~> 3.0'
30
+ spec.add_dependency 'rbs', '>=3.0', '<5'
31
31
 
32
32
  spec.add_development_dependency "bundler", "~> 2.0"
33
33
  spec.add_development_dependency "rake", "~> 13.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sord
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.0
4
+ version: 7.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Christiansen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-03-03 00:00:00.000000000 Z
11
+ date: 2025-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: yard
@@ -84,16 +84,22 @@ dependencies:
84
84
  name: rbs
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '3.0'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '5'
90
93
  type: :runtime
91
94
  prerelease: false
92
95
  version_requirements: !ruby/object:Gem::Requirement
93
96
  requirements:
94
- - - "~>"
97
+ - - ">="
95
98
  - !ruby/object:Gem::Version
96
99
  version: '3.0'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '5'
97
103
  - !ruby/object:Gem::Dependency
98
104
  name: bundler
99
105
  requirement: !ruby/object:Gem::Requirement
@@ -174,6 +180,7 @@ extra_rdoc_files: []
174
180
  files:
175
181
  - ".github/ISSUE_TEMPLATE/bug_report.md"
176
182
  - ".github/ISSUE_TEMPLATE/feature-request.md"
183
+ - ".github/workflows/examples.yml"
177
184
  - ".github/workflows/ruby.yml"
178
185
  - ".gitignore"
179
186
  - ".parlour"
@@ -214,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
221
  - !ruby/object:Gem::Version
215
222
  version: '0'
216
223
  requirements: []
217
- rubygems_version: 3.2.22
224
+ rubygems_version: 3.4.1
218
225
  signing_key:
219
226
  specification_version: 4
220
227
  summary: Generate Sorbet RBI files from YARD documentation