solargraph-rails 1.1.2 → 1.2.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/.github/workflows/overcommit.yml +71 -0
- data/.github/workflows/rubocop.yml +31 -0
- data/.github/workflows/ruby.yml +122 -27
- data/.github/workflows/typecheck.yml +81 -0
- data/.overcommit.yml +51 -0
- data/.rubocop.yml +327 -0
- data/.rubocop_todo.yml +857 -0
- data/.solargraph.yml +5 -2
- data/CHANGELOG.md +36 -1
- data/DEVELOPMENT.md +8 -19
- data/Gemfile +20 -8
- data/README.md +38 -25
- data/bin/overcommit +27 -0
- data/bin/rubocop +27 -0
- data/ci/auto_yard/plugins.rb +0 -1
- data/lib/solargraph/rails/annotate.rb +10 -0
- data/lib/solargraph/rails/annotations/action_controller.rb +57 -5
- data/lib/solargraph/rails/annotations/active_model.rb +18 -0
- data/lib/solargraph/rails/annotations/active_record.rb +24 -3
- data/lib/solargraph/rails/annotations/active_support.rb +3 -0
- data/lib/solargraph/rails/annotations/array.rb +3 -0
- data/lib/solargraph/rails/annotations/class.rb +2 -0
- data/lib/solargraph/rails/annotations/module.rb +13 -0
- data/lib/solargraph/rails/annotations/object.rb +3 -0
- data/lib/solargraph/rails/annotations/time.rb +3 -3
- data/lib/solargraph/rails/delegate.rb +44 -10
- data/lib/solargraph/rails/model.rb +252 -31
- data/lib/solargraph/rails/rails_api.rb +2 -2
- data/lib/solargraph/rails/schema.rb +27 -8
- data/lib/solargraph/rails/util.rb +45 -4
- data/lib/solargraph/rails/version.rb +1 -1
- data/lib/solargraph-rails.rb +1 -1
- data/script/generate_definitions.rb +49 -24
- data/solargraph-rails.gemspec +20 -5
- metadata +60 -12
@@ -9,11 +9,35 @@ module Solargraph
|
|
9
9
|
filename.include?('app/models')
|
10
10
|
end
|
11
11
|
|
12
|
+
# @param source_map [Solargraph::SourceMap]
|
13
|
+
# @param ns [Solargraph::Pin::Namespace]
|
12
14
|
def process(source_map, ns)
|
13
15
|
return [] unless self.class.valid_filename?(source_map.filename)
|
14
16
|
|
15
|
-
walker = Walker.from_source(source_map.source)
|
16
17
|
pins = []
|
18
|
+
abstract = false
|
19
|
+
|
20
|
+
# ActiveRecord defines a hidden subclass of ActiveRecord::Relation for
|
21
|
+
# each model class that inherits from ActiveRecord::Base.
|
22
|
+
pins << relation = Solargraph::Pin::Namespace.new(
|
23
|
+
name: 'ActiveRecord_Relation',
|
24
|
+
type: :class,
|
25
|
+
visibility: :private,
|
26
|
+
closure: ns,
|
27
|
+
)
|
28
|
+
pins << Solargraph::Pin::Reference::Superclass.new(
|
29
|
+
name: "ActiveRecord::Relation",
|
30
|
+
closure: relation,
|
31
|
+
)
|
32
|
+
|
33
|
+
pins << Solargraph::Pin::Method.new(
|
34
|
+
name: 'model',
|
35
|
+
scope: :instance,
|
36
|
+
closure: relation,
|
37
|
+
comments: "@return [Class<#{ns.name}>]"
|
38
|
+
)
|
39
|
+
|
40
|
+
walker = Walker.from_source(source_map.source)
|
17
41
|
|
18
42
|
walker.on :send, [nil, :belongs_to] do |ast|
|
19
43
|
pins << singular_association(ns, ast)
|
@@ -31,75 +55,272 @@ module Solargraph
|
|
31
55
|
pins << plural_association(ns, ast)
|
32
56
|
end
|
33
57
|
|
58
|
+
walker.on :send, [:self, :abstract_class=, :true] do |ast|
|
59
|
+
abstract = true
|
60
|
+
end
|
61
|
+
|
34
62
|
walker.on :send, [nil, :scope] do |ast|
|
35
63
|
next if ast.children[2].nil?
|
36
64
|
name = ast.children[2].children.last
|
37
65
|
|
38
|
-
|
39
|
-
Util.build_public_method(
|
40
|
-
ns,
|
41
|
-
name.to_s,
|
42
|
-
types: ns.return_type.map(&:tag),
|
43
|
-
scope: :class,
|
44
|
-
location: Util.build_location(ast, ns.filename)
|
45
|
-
)
|
66
|
+
parameters = []
|
46
67
|
|
47
68
|
if ast.children.last.type == :block
|
48
69
|
location = ast.children.last.location
|
49
|
-
block_pin =
|
50
|
-
|
51
|
-
|
70
|
+
block_pin = source_map.locate_block_pin(location.line, location.column)
|
71
|
+
parameters.concat(block_pin.parameters.clone)
|
72
|
+
block_pin.instance_variable_set(:@binder, ComplexType.parse(relation.path))
|
52
73
|
end
|
53
|
-
|
74
|
+
|
75
|
+
location = Util.build_location(ast, ns.filename)
|
76
|
+
# define scopes as a class methods on the model, and instance methods
|
77
|
+
# on the hidden relation class
|
78
|
+
pins << Util.build_public_method(
|
79
|
+
ns,
|
80
|
+
name.to_s,
|
81
|
+
scope: :class,
|
82
|
+
parameters: parameters,
|
83
|
+
types: [relation_type(ns.name)],
|
84
|
+
location: location
|
85
|
+
)
|
86
|
+
pins << Util.build_public_method(
|
87
|
+
relation,
|
88
|
+
name.to_s,
|
89
|
+
scope: :instance,
|
90
|
+
parameters: parameters,
|
91
|
+
types: [relation_type(ns.name)],
|
92
|
+
location: location
|
93
|
+
)
|
54
94
|
end
|
55
95
|
|
56
96
|
walker.walk
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
97
|
+
|
98
|
+
# Class methods on the model are exposed as *instance* methods on the
|
99
|
+
# hidden ActiveRecord_Relation class.
|
100
|
+
#
|
101
|
+
# Uses DelegatedMethod pins (instead of build_public_method) so Solargraph
|
102
|
+
# will show the "real" method pin for type inference, probing, docs etc.
|
103
|
+
source_map.pins.each do |pin|
|
104
|
+
next unless Delegate.supported?
|
105
|
+
next unless pin.is_a?(Solargraph::Pin::Method) && pin.scope == :class && pin.closure == ns
|
106
|
+
|
107
|
+
pins << Solargraph::Pin::DelegatedMethod.new(closure: relation, scope: :instance, method: pin)
|
108
|
+
end
|
109
|
+
|
110
|
+
unless abstract
|
111
|
+
pins += relation_method_pins(ns, :class, ns.path)
|
112
|
+
pins += relation_method_pins(relation, :instance, ns.path)
|
61
113
|
end
|
114
|
+
|
115
|
+
Solargraph.logger.debug("[Rails][Model] added #{pins.map(&:name)} to #{ns.path}")
|
116
|
+
|
62
117
|
pins
|
63
118
|
end
|
64
119
|
|
120
|
+
|
65
121
|
def plural_association(ns, ast)
|
66
|
-
|
122
|
+
association_name = ast.children[2].children.first
|
67
123
|
class_name =
|
68
124
|
extract_custom_class_name(ast) ||
|
69
|
-
|
125
|
+
association_name.to_s.singularize.camelize
|
70
126
|
|
71
127
|
Util.build_public_method(
|
72
128
|
ns,
|
73
|
-
|
74
|
-
types: [
|
129
|
+
association_name.to_s,
|
130
|
+
types: [relation_type(class_name)],
|
75
131
|
location: Util.build_location(ast, ns.filename)
|
76
132
|
)
|
77
133
|
end
|
78
134
|
|
79
135
|
def singular_association(ns, ast)
|
80
|
-
|
136
|
+
association_name = ast.children[2].children.first
|
81
137
|
class_name =
|
82
|
-
extract_custom_class_name(ast) ||
|
138
|
+
extract_custom_class_name(ast) || association_name.to_s.camelize
|
83
139
|
|
84
140
|
Util.build_public_method(
|
85
141
|
ns,
|
86
|
-
|
142
|
+
association_name.to_s,
|
87
143
|
types: [class_name],
|
88
144
|
location: Util.build_location(ast, ns.filename)
|
89
145
|
)
|
90
146
|
end
|
91
147
|
|
92
148
|
def extract_custom_class_name(ast)
|
93
|
-
|
94
|
-
return unless
|
149
|
+
node = Util.extract_option(ast, :class_name)
|
150
|
+
return unless node && node.type == :str
|
151
|
+
|
152
|
+
node.children.last
|
153
|
+
end
|
154
|
+
|
155
|
+
# Generate method pins for ActiveRecord methods in the given namespace/scope, where the
|
156
|
+
# the return types will be templated with the provided model class.
|
157
|
+
#
|
158
|
+
# These method pins don't need to include any documentation, as Solargraph will merge
|
159
|
+
# documentation from Rails when it resolves the "method stack" for each pin.
|
160
|
+
#
|
161
|
+
# @param ns [Solargraph::Pin::Namespace] the namespace (model or relation class) in which to define methods.
|
162
|
+
# @param scope [:instance, :class] the method scope (:class for the model and :instance for the relation).
|
163
|
+
# @param model_class [String] the model class (e.g. "Person") that should be used in return types.
|
164
|
+
# @return [Array<Solargraph::Pin::Method>]
|
165
|
+
def relation_method_pins(namespace, scope, model_class)
|
166
|
+
pins = []
|
167
|
+
finalize_type = ->(template) { template.gsub '$T', model_class }
|
168
|
+
RETURNS_RELATION.each do |method_name, params|
|
169
|
+
next if OVERLOADED.key(method_name)
|
95
170
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
171
|
+
method = Util.build_public_method(namespace, method_name, scope: scope, parameters: [], types: [relation_type(model_class)])
|
172
|
+
params.each do |name, type|
|
173
|
+
decl = :arg
|
174
|
+
# TODO: maybe I can remove this and go back to letting solargraph parse a comment block
|
175
|
+
# @see https://github.com/castwide/solargraph/pull/601
|
176
|
+
if name.start_with?('**')
|
177
|
+
name = name[2..]
|
178
|
+
decl = :kwrestarg
|
179
|
+
elsif name.start_with?('*')
|
180
|
+
name = name[1..]
|
181
|
+
decl = :restarg
|
182
|
+
end
|
183
|
+
method.parameters << Solargraph::Pin::Parameter.new(name: name, decl: decl, closure: method,
|
184
|
+
comments: "@return [#{type}]")
|
100
185
|
end
|
101
|
-
|
186
|
+
pins << method
|
187
|
+
end
|
188
|
+
|
189
|
+
RETURNS_INSTANCE.each do |method|
|
190
|
+
pins << Util.build_public_method(namespace, method, scope: scope, types: [model_class])
|
191
|
+
end
|
192
|
+
|
193
|
+
OVERLOADED.each do |method, overloads|
|
194
|
+
comments = overloads.map do |arg_list, lines|
|
195
|
+
lines = ["@return [#{lines}]"] if lines.is_a?(String)
|
196
|
+
lines = ["@overload #{method}#{arg_list}"] + lines
|
197
|
+
lines.map(&finalize_type).join("\n ")
|
198
|
+
end
|
199
|
+
pins << Util.build_public_method(namespace, method, scope: scope, comments: comments.join("\n"))
|
200
|
+
end
|
201
|
+
pins
|
202
|
+
end
|
203
|
+
|
204
|
+
# construct the type name for the models hidden relation class.
|
205
|
+
# the additional type parameter is _not_ redundant, it makes enumerable methods work.
|
206
|
+
def relation_type(model_path)
|
207
|
+
"#{model_path}::ActiveRecord_Relation"
|
102
208
|
end
|
209
|
+
|
210
|
+
ANY_ARGS = {"*args" => nil}
|
211
|
+
|
212
|
+
# @todo can this be replaced with rbs_collection's
|
213
|
+
# _ActiveRecord_Relation interface? Potential leads:
|
214
|
+
# https://github.com/pocke/rbs_rails
|
215
|
+
# https://github.com/ruby/gem_rbs_collection/blob/6f2d93ab244008bec51db7b4fffae68b40232502/gems/paranoia/2.5/paranoia.rbs#L6C1-L7C1
|
216
|
+
# https://github.com/ksss/orthoses-rails
|
217
|
+
# https://github.com/PawCorp/walltaker/blob/728682baa56a267611aca1ddb9f44fdcd97f6c80/sig/rbs_rails/app/models/user.rbs#L2
|
218
|
+
RETURNS_RELATION = {
|
219
|
+
"all" => {},
|
220
|
+
"and" => {"other" => "ActiveRecord::Relation"},
|
221
|
+
"annotate" => ANY_ARGS,
|
222
|
+
"distinct" => ANY_ARGS,
|
223
|
+
"eager_load" => ANY_ARGS,
|
224
|
+
"excluding" => ANY_ARGS,
|
225
|
+
"from" => {"source" => "ActiveRecord::Relation"},
|
226
|
+
"group" => ANY_ARGS,
|
227
|
+
"having" => ANY_ARGS,
|
228
|
+
"in_order_of" => ANY_ARGS,
|
229
|
+
"includes" => ANY_ARGS,
|
230
|
+
"invert_where" => {},
|
231
|
+
"joins" => ANY_ARGS,
|
232
|
+
"left_joins" => ANY_ARGS,
|
233
|
+
"left_outer_joins" => ANY_ARGS,
|
234
|
+
"limit" => {"value" => "Integer"},
|
235
|
+
"lock" => {"locks" => "true, false"},
|
236
|
+
"none" => {},
|
237
|
+
"offset" => {"value" => "Integer"},
|
238
|
+
"or" => {"other" => "ActiveRecord::Relation"},
|
239
|
+
"order" => ANY_ARGS,
|
240
|
+
"preload" => ANY_ARGS,
|
241
|
+
"readonly" => {"value" => "true, false"},
|
242
|
+
"references" => {"*table_names" => nil},
|
243
|
+
"reorder" => ANY_ARGS,
|
244
|
+
"reselect" => ANY_ARGS,
|
245
|
+
"reverse_order" => {},
|
246
|
+
"rewhere" => {"conditions" => "Hash"},
|
247
|
+
"select" => ANY_ARGS,
|
248
|
+
"strict_loading" => {"value" => "true, false"},
|
249
|
+
"unscope" => ANY_ARGS,
|
250
|
+
"without" => ANY_ARGS,
|
251
|
+
}
|
252
|
+
|
253
|
+
RETURNS_INSTANCE = %w[
|
254
|
+
take take! sole
|
255
|
+
first second third fourth fifth third_to_last second_to_last last
|
256
|
+
first! second! third! fourth! fifth! third_to_last! second_to_last! last!
|
257
|
+
forty_two forty_two!
|
258
|
+
]
|
259
|
+
|
260
|
+
OVERLOADED = {
|
261
|
+
"where" => {
|
262
|
+
"()" => "ActiveRecord::QueryMethods::WhereChain<$T::ActiveRecord_Relation>",
|
263
|
+
"(sql, *args)" => [
|
264
|
+
"@return [$T::ActiveRecord_Relation]",
|
265
|
+
],
|
266
|
+
},
|
267
|
+
"select" => {
|
268
|
+
"()" => [
|
269
|
+
"@yieldparam [$T]",
|
270
|
+
"@return [Array<$T>]",
|
271
|
+
],
|
272
|
+
"(*args)" => "$T::ActiveRecord_Relation",
|
273
|
+
},
|
274
|
+
"find" => {
|
275
|
+
"(id)" => [
|
276
|
+
"@param id [Integer, String]",
|
277
|
+
"@return [$T]"
|
278
|
+
],
|
279
|
+
"(*ids)" => "Array<$T>",
|
280
|
+
},
|
281
|
+
"find_by" => {
|
282
|
+
"(hash)" => [
|
283
|
+
"@param hash [Hash] attributes to match by",
|
284
|
+
"@return [$T, nil]",
|
285
|
+
],
|
286
|
+
"(sql, *args)" => [
|
287
|
+
"@param sql [String] a SQL snippet for the WHERE clause",
|
288
|
+
"@return [$T, nil]"
|
289
|
+
]
|
290
|
+
},
|
291
|
+
"find_by!" => {
|
292
|
+
"(hash)" => [
|
293
|
+
"@param hash [Hash] attributes to match by",
|
294
|
+
"@return [$T]",
|
295
|
+
],
|
296
|
+
"(sql, *args)" => [
|
297
|
+
"@param sql [String] a SQL snippet for the WHERE clause",
|
298
|
+
"@return [$T]"
|
299
|
+
]
|
300
|
+
},
|
301
|
+
"find_sole_by" => {
|
302
|
+
"(hash)" => [
|
303
|
+
"@param hash [Hash] attributes to match by",
|
304
|
+
"@return [$T]",
|
305
|
+
],
|
306
|
+
"(sql, *args)" => [
|
307
|
+
"@param sql [String] a SQL snippet for the WHERE clause",
|
308
|
+
"@return [$T]"
|
309
|
+
]
|
310
|
+
},
|
311
|
+
"take" => {
|
312
|
+
"()" => "T, nil",
|
313
|
+
"(limit)" => "Array<$T>",
|
314
|
+
},
|
315
|
+
"first" => {
|
316
|
+
"()" => "$T, nil",
|
317
|
+
"(limit)" => "Array<$T>",
|
318
|
+
},
|
319
|
+
"last" => {
|
320
|
+
"()" => "$T, nil",
|
321
|
+
"(limit)" => "Array<$T>"
|
322
|
+
},
|
323
|
+
}
|
103
324
|
end
|
104
325
|
end
|
105
326
|
end
|
@@ -43,15 +43,34 @@ module Solargraph
|
|
43
43
|
|
44
44
|
return [] unless table
|
45
45
|
|
46
|
-
pins =
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
)
|
46
|
+
pins = []
|
47
|
+
table.each do |column, data|
|
48
|
+
location = Util.build_location(data.ast, 'db/schema.rb')
|
49
|
+
type = RUBY_TYPES[data.type.to_sym] || 'String'
|
50
|
+
%w[% %_in_database %_before_last_save].each do |tpl|
|
51
|
+
name = tpl.sub('%', column)
|
52
|
+
pins << Util.build_public_method(ns, name, types: [type], location: location)
|
54
53
|
end
|
54
|
+
%w[%? %_changed? saved_change_to_%? will_save_change_to_%?].each do |tpl|
|
55
|
+
name = tpl.sub('%', column)
|
56
|
+
pins << Util.build_public_method(ns, name, types: ['Boolean'], location: location)
|
57
|
+
end
|
58
|
+
%w[%_change_to_be_saved saved_change_to_%].each do |tpl|
|
59
|
+
name = tpl.sub('%', column)
|
60
|
+
types = ["Array(#{type}, #{type})"]
|
61
|
+
pins << Util.build_public_method(ns, name, types: types, location: location)
|
62
|
+
end
|
63
|
+
pins << Util.build_public_method(ns, "#{column}=", types: [type],
|
64
|
+
params: { 'value' => [type] },
|
65
|
+
location: location)
|
66
|
+
|
67
|
+
# Note on this: its the starting step of dynamic filters. Technically, you can also do Model.find_by_col1_and_col2(val1, val2)
|
68
|
+
# However, if we start suggestion all of those possibilities, it will simply be bad UX because of having too many suggestions
|
69
|
+
pins << Util.build_public_method(ns, "find_by_#{column}", types: ['self', 'nil'],
|
70
|
+
params: { 'value' => [type] },
|
71
|
+
location: location,
|
72
|
+
scope: :class)
|
73
|
+
end
|
55
74
|
|
56
75
|
if pins.any?
|
57
76
|
Solargraph.logger.debug(
|
@@ -4,25 +4,45 @@ module Solargraph
|
|
4
4
|
def self.build_public_method(
|
5
5
|
ns,
|
6
6
|
name,
|
7
|
+
comments: nil,
|
8
|
+
parameters: [],
|
7
9
|
types: nil,
|
10
|
+
params: {},
|
8
11
|
location: nil,
|
9
12
|
attribute: false,
|
10
13
|
scope: :instance
|
11
14
|
)
|
12
15
|
opts = {
|
13
16
|
name: name,
|
17
|
+
parameters: parameters,
|
14
18
|
location: location,
|
15
19
|
closure: ns,
|
16
20
|
scope: scope,
|
17
21
|
attribute: attribute
|
18
22
|
}
|
19
23
|
|
20
|
-
|
21
|
-
|
24
|
+
comments_arr = [comments].compact
|
25
|
+
params.each do |name, types|
|
26
|
+
comments_arr << "@param [#{types.join(',')}] #{name}"
|
27
|
+
end
|
28
|
+
comments_arr << "@return [#{types.join(',')}]" if types
|
22
29
|
|
23
|
-
opts[:comments]
|
30
|
+
opts[:comments] ||= comments_arr.join("\n")
|
24
31
|
|
25
|
-
Solargraph::Pin::Method.new(**opts)
|
32
|
+
m = Solargraph::Pin::Method.new(**opts)
|
33
|
+
parameters = parameters + params.map do |name, type|
|
34
|
+
Solargraph::Pin::Parameter.new(
|
35
|
+
location: nil,
|
36
|
+
closure: m,
|
37
|
+
comments: '',
|
38
|
+
name: name,
|
39
|
+
presence: nil,
|
40
|
+
decl: :arg,
|
41
|
+
asgn_code: nil
|
42
|
+
)
|
43
|
+
end
|
44
|
+
m.parameters.concat(parameters)
|
45
|
+
m
|
26
46
|
end
|
27
47
|
|
28
48
|
def self.build_module_include(ns, module_name, location)
|
@@ -63,6 +83,27 @@ module Solargraph
|
|
63
83
|
def self.method_return(path, type)
|
64
84
|
Solargraph::Pin::Reference::Override.method_return(path, type)
|
65
85
|
end
|
86
|
+
|
87
|
+
# Extract the value of a given option from a :send syntax node.
|
88
|
+
#
|
89
|
+
# E.g. given an AST node for `foo(:bar, baz: qux)`, you can use
|
90
|
+
# `extract_option(node, :baz)` to get the AST node for `qux`.
|
91
|
+
#
|
92
|
+
# @param call_node [AST::Node]
|
93
|
+
# @param option_name [Symbol]
|
94
|
+
# @return [AST::Node, nil]
|
95
|
+
def self.extract_option(call_node, option_name)
|
96
|
+
options = call_node.children[3..-1].find { |n| n.type == :hash }
|
97
|
+
return unless options
|
98
|
+
|
99
|
+
pair =
|
100
|
+
options.children.find do |n|
|
101
|
+
n.children[0] && n.children[0].deconstruct == [:sym, option_name]
|
102
|
+
end
|
103
|
+
return unless pair
|
104
|
+
|
105
|
+
pair.children[1]
|
106
|
+
end
|
66
107
|
end
|
67
108
|
end
|
68
109
|
end
|
data/lib/solargraph-rails.rb
CHANGED
@@ -50,7 +50,7 @@ module Solargraph
|
|
50
50
|
pins += run_feature { Storage.instance.process(source_map, ns) }
|
51
51
|
pins += run_feature { Autoload.instance.process(source_map, ns, ds) }
|
52
52
|
pins += run_feature { Devise.instance.process(source_map, ns) }
|
53
|
-
pins += run_feature { Delegate.instance.process(source_map, ns) }
|
53
|
+
pins += run_feature { Delegate.instance.process(source_map, ns) } if Delegate.supported?
|
54
54
|
pins += run_feature { RailsApi.instance.local(source_map, ns) }
|
55
55
|
|
56
56
|
Solargraph::Environ.new(pins: pins)
|
@@ -1,28 +1,42 @@
|
|
1
1
|
require File.join(Dir.pwd, ARGV.first, 'config/environment')
|
2
2
|
|
3
|
-
|
3
|
+
require 'method_source'
|
4
|
+
|
5
|
+
def _instance_methods(klass, test = klass.new)
|
6
|
+
klass
|
7
|
+
.instance_methods(true)
|
8
|
+
.sort
|
9
|
+
.reject { |m| m.to_s.start_with?('_') || (test && !test.respond_to?(m)) }
|
10
|
+
.map { |m| klass.instance_method(m) }
|
4
11
|
end
|
5
12
|
|
6
13
|
def own_instance_methods(klass, test = klass.new)
|
7
|
-
|
8
|
-
|
14
|
+
reject_meth_names = (Module.methods + Module.private_methods + [:to_yaml, :to_json]).to_set
|
15
|
+
_instance_methods(klass, test).select do |m|
|
16
|
+
!reject_meth_names.include?(m.name) &&
|
17
|
+
m.source_location &&
|
18
|
+
m.source_location.first.include?('gem') &&
|
19
|
+
m.source_location &&
|
20
|
+
m.source_location.first.include?('gem') &&
|
21
|
+
!m.source_location.first.include?('/pp-') &&
|
22
|
+
m.comment &&
|
23
|
+
!m.comment.empty? &&
|
24
|
+
m.comment != ':nodoc:'
|
9
25
|
end
|
10
26
|
end
|
11
27
|
|
12
28
|
def own_class_methods(klass)
|
13
|
-
|
14
|
-
|
29
|
+
reject_meth_names = (Module.methods + Module.private_methods + [:to_yaml, :to_json]).to_set
|
30
|
+
class_methods(klass).select do |m|
|
31
|
+
!reject_meth_names.include?(m.name) &&
|
32
|
+
m.source_location &&
|
33
|
+
m.source_location.first.include?('gem') &&
|
34
|
+
m.comment &&
|
35
|
+
!m.comment.empty? &&
|
36
|
+
m.comment != ':nodoc:'
|
15
37
|
end
|
16
38
|
end
|
17
39
|
|
18
|
-
def instance_methods(klass, test = klass.new)
|
19
|
-
klass
|
20
|
-
.instance_methods(true)
|
21
|
-
.sort
|
22
|
-
.reject { |m| m.to_s.start_with?('_') || (test && !test.respond_to?(m)) }
|
23
|
-
.map { |m| klass.instance_method(m) }
|
24
|
-
end
|
25
|
-
|
26
40
|
def class_methods(klass)
|
27
41
|
klass
|
28
42
|
.methods(true)
|
@@ -53,6 +67,18 @@ def build_report(klass, test: klass.new)
|
|
53
67
|
result
|
54
68
|
end
|
55
69
|
|
70
|
+
def add_new_methods(klass, test, yaml_filename)
|
71
|
+
new_report = build_report(klass, test: test)
|
72
|
+
existing_report = {}
|
73
|
+
existing_report = YAML.load_file(yaml_filename) if File.exist?(yaml_filename)
|
74
|
+
report = { **new_report, **existing_report }
|
75
|
+
class_methods, instance_methods = report.partition { |k, _| k.include?('.') }
|
76
|
+
class_methods = class_methods.sort_by { |k, _v| k }.to_h
|
77
|
+
instance_methods = instance_methods.sort_by { |k, _v| k }.to_h
|
78
|
+
report = { **class_methods, **instance_methods }
|
79
|
+
File.write(yaml_filename, report.deep_stringify_keys.to_yaml)
|
80
|
+
end
|
81
|
+
|
56
82
|
def core_ext_report(klass, test = klass.new)
|
57
83
|
result = {}
|
58
84
|
distribution = {}
|
@@ -73,7 +99,7 @@ def core_ext_report(klass, test = klass.new)
|
|
73
99
|
}
|
74
100
|
end
|
75
101
|
|
76
|
-
|
102
|
+
_instance_methods(klass, test)
|
77
103
|
.select(&:source_location)
|
78
104
|
.select do |meth|
|
79
105
|
loc = meth.source_location.first
|
@@ -92,18 +118,18 @@ def core_ext_report(klass, test = klass.new)
|
|
92
118
|
result
|
93
119
|
end
|
94
120
|
|
95
|
-
|
96
|
-
|
121
|
+
add_new_methods(ActiveRecord::Base, Model.new, '../definitions/activerecord.yml')
|
122
|
+
add_new_methods(ActionController::Base, false, '../definitions/actioncontroller.yml')
|
123
|
+
add_new_methods(ActiveJob::Base, false, '../definitions/activejob.yml')
|
97
124
|
|
98
|
-
|
99
|
-
|
125
|
+
Rails.application.routes.draw do
|
126
|
+
add_new_methods(self.class, false, '../definitions/routes.yml')
|
127
|
+
end
|
100
128
|
|
101
|
-
|
102
|
-
File.write('activejob.yml', report.deep_stringify_keys.to_yaml)
|
129
|
+
add_new_methods(Rails::Application, false, '../definitions/application.yml')
|
103
130
|
|
104
131
|
Rails.application.routes.draw do
|
105
|
-
|
106
|
-
File.write('routes.yml', report.deep_stringify_keys.to_yaml)
|
132
|
+
add_new_methods(self.class, false, '../definitions/routes.yml')
|
107
133
|
end
|
108
134
|
|
109
135
|
[
|
@@ -134,8 +160,7 @@ end
|
|
134
160
|
end
|
135
161
|
end
|
136
162
|
|
137
|
-
|
138
|
-
File.write("#{klass.to_s}.yml", report.deep_stringify_keys.to_yaml)
|
163
|
+
add_new_methods(klass, test, "../definitions/core/#{klass.to_s}.yml")
|
139
164
|
end
|
140
165
|
|
141
166
|
# binding.pry
|
data/solargraph-rails.gemspec
CHANGED
@@ -4,7 +4,23 @@ require 'solargraph/rails/version'
|
|
4
4
|
|
5
5
|
|
6
6
|
solargraph_force_ci_version = (ENV['CI'] && ENV['MATRIX_SOLARGRAPH_VERSION'])
|
7
|
-
solargraph_version =
|
7
|
+
solargraph_version =
|
8
|
+
if solargraph_force_ci_version
|
9
|
+
[solargraph_force_ci_version]
|
10
|
+
else
|
11
|
+
[
|
12
|
+
# below this isn't tested in CI
|
13
|
+
'>= 0.48.0',
|
14
|
+
# did not have support for global conventions
|
15
|
+
'!=0.53.0', '!=0.53.1', '!=0.53.2', '!=0.53.3', '!=0.53.4',
|
16
|
+
# did not have support for global conventions
|
17
|
+
'!=0.54.0', '!=0.54.1', '!=0.54.2', '!=0.54.3', '!=0.54.4', '!=0.54.5',
|
18
|
+
# did not have support for global conventions
|
19
|
+
'!=0.55.0', '!=0.55.1', '!=0.55.2', '!=0.55.3', '!=0.55.4', '!=0.55.5', '!=0.55.6', '!=0.55.7',
|
20
|
+
# above this hasn't been tested
|
21
|
+
'< 0.57'
|
22
|
+
]
|
23
|
+
end
|
8
24
|
|
9
25
|
Gem::Specification.new do |spec|
|
10
26
|
spec.name = 'solargraph-rails'
|
@@ -30,11 +46,10 @@ Gem::Specification.new do |spec|
|
|
30
46
|
spec.add_development_dependency 'bundler', '~> 2.3'
|
31
47
|
spec.add_development_dependency 'rake', '~> 12.3.3'
|
32
48
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
49
|
+
spec.add_development_dependency 'rubocop', '~> 1.76'
|
50
|
+
spec.add_development_dependency 'simplecov', '~> 0.22.0'
|
51
|
+
spec.add_development_dependency 'simplecov-lcov', '~> 0.8.0'
|
33
52
|
|
34
|
-
# Solargraph temporarily dropped support for Convention.global in
|
35
|
-
# 0.53 - this should be lifted after it returns:
|
36
|
-
#
|
37
|
-
# https://github.com/castwide/solargraph/pull/877
|
38
53
|
spec.add_runtime_dependency 'solargraph', *solargraph_version
|
39
54
|
|
40
55
|
spec.add_runtime_dependency 'activesupport'
|