Closed egiurleo closed 2 years ago
I'm not sure if the implementation in my head is the best solution, so I want to discuss it here:
Class (or module)
"https://api.rubyonrails.org/#{version}/classes/#{namespaces.join("/")}.html"
Method
"https://api.rubyonrails.org/#{version}/classes/#{namespaces.join("/")}.html#method-#{instance_or_class}-#{method_name}"
activesupport
): file name (e.g. activesupport@7.0.3.1.rbi
)
7.0.3.1
): file name (e.g. activesupport@7.0.3.1.rbi
)
@st0012 This sounds like a really good plan to me! I have one question: would it be possible to eliminate the use of the stack by using things like the Object#const_source_location
or the Method#owner
to discover the namespaces of each const and method?
I think it's not possible because we're not actually evaluating the code? We can only access names instead of the objects from syntax tree AFAIK.
Oh, it's unfortunate that the classes and modules are a part of the URL. However, we don't need to add document links for every single method exported by Rails. Just providing this for the most common ones will probably already provide quite a bit of value.
What do you think about doing something like this
class DocumentLink < BaseRequest
BASE_RAILS_DOC_URL = "https://api.rubyonrails.org/v#{RAILS_VERSION}" # read the rails version from the stubs
METHOD_TO_DOC_URL = {
"belongs_to" => "classes/ActiveRecord/Associations/ClassMethods.html"
}
def visit_command(node) # Not sure if it's command, just an example
suffix = METHOD_TO_DOC_URL[node.value] # or node.message something like that
return if suffix.nil? # We don't know the doc URL for this one
target = "#{BASE_RAILS_DOC_URL}/#{suffix}#method-i-#{node.value}"
# create link from target
end
end
I suggest prioritizing the following. You can start by implementing only one of them in an initial PR (e.g.: only belongs_to) to get feedback on the logic. After we settled on that, it's just a matter of adding more entries to the hash in follow up PRs.
Rails API guides have a search mechanism which relies on the data stored in the search_index.js
file. The data is basically JSON, which we can use to match method names to discover the full doc URL.
For example, the info
hash contains the following for run_callbacks
:
// ...
[
"run_callbacks",
"ActiveSupport::Callbacks",
"classes/ActiveSupport/Callbacks.html#method-i-run_callbacks",
"(kind)",
"<p>Runs the callbacks for the given event.\n<p>Calls the before and around callbacks in the order they were set, …\n"
],
// ...
@vinistock I think the white listing approach is not ideal for Rails documentation as there are many methods and components that could use this feature. So the list will end up very long and we'll need to manually select things to be added. I'd like to avoid this if possible.
@paracycle The search_index.js
is very interesting and I didn't know it exists until now. For anyone who's curious, the file is generated by rdoc
so it's not Rails-specific. I thought about using it as a database, but since most of the data are stored in arrays, we may need to transform it first (or bsearch
on elements?).
I'll prototype the proposed flow to see if the current rbi
files can provide reliable context for generating the urls. If not, I'll use search_index.js
to do that.
As another aspect to this issue, can we also consider adding comments to the generated RBIs for DSLs in Tapioca?
Thanks to the DSL compilers, a lot of the methods created through Rails meta-programming get reified in the RBI files and appear when the user overs them.
We could make the DSL compilers attach comments to the generated RBI nodes so it explains where the things come from and points to the documentation of the feature.
We can use information about the method, such as the class that defines it and its ancestor chain, as well as information about the rails gem (version number) to link to the correct documentation page for that method.
Consider this:
# file1.rb
class CustomJob < ApplicationJob
# ...
end
# file2.rb
class SomeJob < CustomJob
# ...
end
Only considering file2.rb
won't give you much information about what you could document inside SomeJob
, you would need to resolve CustomJob
and then find out that it inherits ApplicationJob
.
If we want to rely on the model we will have to know about the whole code base and I fear that anything not based on Sorbet will be too slow.
In the meantime we can prototype with the whitelist approach but we may link things that are unrelated by accident.
I thought this link will only be activated in rbi
files? Because the current flow is like:
rbi
filesource
comment to visit the sourceSo I think the Rails document link will only happen in the rbi
file too:
rbi
filebelongs_to
, it'll show the user an option to visit the documentIf we try to resolve the method source from application code directly, I think it'd be too inaccurate.
After discussing with @vinistock we had these conclusion:
app/models/post.rb
. Not the rbi
files I assumed.search_index
file mentioned by @paracycle as the data source.
net/http
and be stored inside a constant.belongs_to
is common in API serialisation tools. We may need to use the current file's path to decide if we should show the link. For example, if it's not under app/models/
, we don't show links for anything under ActiveRecord
's namespaces.ActiveSupport
and ActionView
at the beginning because it could be hard to determine if the methods are actually from them.Ok is cool
We implemented the DocumentLink request in #58 and used it to go to method definition from gem RBIs.
As a next step, let's use this request to link to the correct Rails documentation for methods that come from the rails gem.
When a user hovers over a method that's defined in rails, there should be a link that says "go to documentation." We can use information about the method, such as the class that defines it and its ancestor chain, as well as information about the rails gem (version number) to link to the correct documentation page for that method.