rdoc icon indicating copy to clipboard operation
rdoc copied to clipboard

Make it loose coupling between RubyGems and RDoc

Open mterada1228 opened this issue 1 year ago • 14 comments

Problems

There are following problems because of tight coupling between RubyGems and RDoc.

  1. If there are braking changes in RDoc, RubyGems is also broken.
  2. When we maintain RDoc, we have to change RubyGems.

The reason why they are happened is that RubyGems creates documents about a gem with installing it.

Note that RubyGems uses functions of RDoc to create documents. Specifically,

  • Creating documents is executed by rubygems/lib/rubygems/rdoc.rb.
  • ::RDoc::RubygemsHook which is defined by RDoc is called by the file.

Solution

RubyGems has the plugin system.

If a gem includes rubygems_plugin.rb, RubyGems loads it. RubyGems executes a process defined in it while installing gems, uninstalling gems or other events.

We can use the system to solve the problems.

The root cause is RubyGems directly references the class of RDoc.

We can remove the root cause by making RDoc RubyGems plugin.

Alternatively rubygems_plugin.rb creates documents about gems.

FAQ

Q1. Do we need to change codes of RubyGems?

A.

No, we don't.

This change keeps compatibility of API used from RubyGems.

Q2. Is it better to delete existing codes related to RDoc in RubyGems?

No, it isn't.

If we change codes of RubyGems, we can't keep a compatibility.

Example:

If we delete codes that uses RDoc::RubygemsHook in rubygems/lib/rubygems/rdoc.rb, documentations are not created with old RDoc.

Q3. When can we delete rubygems/lib/rubygems/rdoc.rb?

A.

We can delete it when all users use RDoc including rubygems_plugin.

Next ruby version is 3.4. If it includes the RDoc including rubygems_plugin, we can delete rubygems/lib/rubygems/rdoc.rb after ruby 3.3 is EOL.

Q4. Is it a breaking change that Rubygems creates documents with rubygems_plugin not RDoc::RubygemsHook?

A.

No, it isn't.

If we simply implement this approach, we move the implementation from rdoc/lib/rdoc/rubygems_hook.rb to rubygems_plugin.rb.

This way can be breaking change.

It seems to be fine that we just need to delete rdoc/rubygems_hook.rb but it doesn't work. It generates multiple documents.

rubygems/lib/rubygems/rdoc.rb has the following code.

begin
  require "rdoc/rubygems_hook"
  # ...
rescue LoadError
end

This code ignores RDoc related processes when rdoc/rubygems_hook can't be required. But, this 'require' is not failed.

This is because Ruby installs Rdoc as a default gem.

So, Rdoc installed as a default gem generates documents and one installed as a normal gem does it too.

If you think that this behavior is accectable, we can just delete rdoc/rubygems_hook.rb.

What do you think about this approach?

In this change, we take another approach to solve the problem that creates multiple documents.

If Gem.done_installing(&Gem::RDoc.method(:generation_hook)) in rubygems/rdoc.rb doesn't create documents, we can solve the problem.

We have some options.

  • We change rubygems/rdoc.rb and then don't execute Gem.done_installing. (This is a change for RubyGems.)
  • We change rdoc/rubygems_hook.rb and then make generation_hook a no-op method. (This is a change for RDoc.)

We choose the latter to avoid changing for RubyGems.

Test

Preparation

Install Rdoc which including our changes by executing rake install.

❯ rake install

We confirmed that Rdoc which including our changes was installed.

❯ gem list | grep rdoc rdoc (6.6.0, default: 6.4.0)

Check point

We tested to check compatibility.

How to chack the compatibility?

We tested creating same documents by our RDoc and old RDoc with latest RubyGems.

We used following versions to test.

❯ ruby -v
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [arm64-darwin22]

❯ gem list | grep rdoc
rdoc (default: 6.4.0)

❯ ruby -I rubygems/lib rubygems/exe/gem --version
3.5.14

Here is a result of test with old RDoc. We can see that the document is created correctlly with Parsing... and Done installing....

❯ ruby -I rubygems/lib rubygems/exe/gem install pkg-config
Successfully installed pkg-config-1.5.6
Parsing documentation for pkg-config-1.5.6
Done installing documentation for pkg-config after 0 seconds
1 gem installed

Here is a result of test with our RDoc. We can see that the document is created correctlly with Parsing... and Done installing....

❯ ruby -I rubygems/lib rubygems/exe/gem install pkg-config
Successfully installed pkg-config-1.5.6
Parsing documentation for pkg-config-1.5.6
Done installing documentation for pkg-config after 0 seconds
1 gem installed

As you can see we got the same results, our RDoc keeps compatibility.

mterada1228 avatar Sep 05 '24 23:09 mterada1228

FYI: RubyGems wants to remove RDoc integration code if possible:

  • https://github.com/orgs/rubygems/discussions/6021
  • https://github.com/rubygems/rubygems/pull/6673#discussion_r1205298579

kou avatar Sep 11 '24 03:09 kou

Thank you @mterada1228 @kou. My current understanding is: this PR will allow RubyGems to remove RDoc specific code, because RDoc will start using RubyGems plugin (its official API) to generate documentation for gems. Is this correct?

st0012 avatar Sep 12 '24 11:09 st0012

Thank you @mterada1228 @kou. My current understanding is: this PR will allow RubyGems to remove RDoc specific code, because RDoc will start using RubyGems plugin (its official API) to generate documentation for gems. Is this correct?

@st0012 Cc: @kou

Yes, it is. Your understanding is correct.

mterada1228 avatar Sep 12 '24 23:09 mterada1228

Correct as @mterada1228 said.

Note: But we can't remove the RDoc integration code from RubyGems immediately as the PR description mentioned. Because:

  • We can't use the RubyGems plugin feature for a default gem
  • RDoc that doesn't have this RubyGems plugin support exist

RDoc will be a bundled gem (not a default gem) since Ruby 3.5: https://bugs.ruby-lang.org/issues/20309 We can remove the RDoc integration code from RubyGems when Ruby 3.4 reaches EOL. We can assume that all maintained RDocs are installed as a normal gem and they have this RubyGems plugin support at the time.

kou avatar Sep 13 '24 01:09 kou

Hello! From RubyGems maintainers team perspective, I think this is a nice thought out PR, and I'm very happy it will allow us to eventually remove RDoc code from RubyGems ❤️. Thank you!

deivid-rodriguez avatar Sep 27 '24 14:09 deivid-rodriguez

Are there people who want to review this PR? If nobody wants to this, I'll merge this in this month. (I'll review this again before I merge this.)

kou avatar Oct 17 '24 01:10 kou

I'm +1 to this change. But I have some concerns. Is this working with fresh installation of ruby package?

In my understanding, the current rubygems plugin system could work with GEM_HOME/plugins/rubygems-generate_index_plugin.rb and GEM_HOME/plugins/rubygems-generate_index-1.1.3/lib/rubygems_plugin.rb with latest rubygems.

How generate GEM_HOME/plugins/rdoc_plugin.rb with fresh installation of Ruby 3.4?

hsbt avatar Oct 18 '24 07:10 hsbt

Does "fresh installation" mean that RDoc is installed as a default gem? This solution (plugin based RDoc generation) isn't used for the case. RDoc hook in RubyGems is used. It's the current approach. This solution is only used when RDoc is installed as a bundled gem or a normal gem.

kou avatar Oct 18 '24 07:10 kou

Does "fresh installation" mean that RDoc is installed as a default gem?

Yes.

This solution is only used when RDoc is installed as a bundled gem or a normal gem.

I see. Thanks. We make document generation is disabled with "RDoc is installed as a default gem" case like Ruby 3.4?

hsbt avatar Oct 18 '24 07:10 hsbt

We make document generation is disabled with "RDoc is installed as a default gem" case like Ruby 3.4?

Does this mean the following?

If we ship this change in Ruby 3.4, whether gem install XXX generates documentation of XXX or not?

The answer is that gem install XXX still generates documentation of XXX. This doesn't change the current behavior. In the case, https://github.com/rubygems/rubygems/blob/465bfaf530c6712487495419e8ce9f5a0a0385e9/lib/rubygems/rdoc.rb#L11 generates documentation of XXX. (This solution isn't used.)

kou avatar Oct 18 '24 07:10 kou

The answer is that gem install XXX still generates documentation of XXX.

Really? https://github.com/ruby/rdoc/pull/1171/files#diff-57670ce1156b5670b904c97f8a01d658a0b5a458ffe71f2fcc7a7eefd4254d2cR15 seems empty.

Could you explain how work lib/rubygems/rdoc.rb and this change without plugin installation?

hsbt avatar Oct 18 '24 08:10 hsbt

Ah, wait. It may not work. @mterada1228 Did we check the case?

kou avatar Oct 18 '24 08:10 kou

@hsbt raised a good point. I think ruby-core default gem installer may need to make sure to generate plugins for default gems too, because as of now, no default gem includes rubygems plugins and I believe the mechanism is not going to work by default.

deivid-rodriguez avatar Oct 21 '24 12:10 deivid-rodriguez

@hsbt, @kou

I checked the behavior in the following case. https://github.com/ruby/rdoc/pull/1171#issuecomment-2421606742

When RDoc includes this changes was used as default gem, documents weren't created as you say.

How to check

  1. Copy codes from this directory to the directory of default gems library.
❯ mv $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc_bkup # for backup
❯ cp -r ./lib/rdoc $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc
  1. Execute gem install.
❯ gem install foo
Fetching foo.gem
Successfully installed foo
1 gem installed

Documents was not created.

Solutions

One solution is that If RDoc::RubygemsHook can find rubygems_plugin.rb RDoc creates documents by rubygems plugins, otherwise RDoc does so by current process. Another solution is that I wait for Ruby 3.5 will make RDoc from default gems to bundled gems.

Although It is possible that I make default gems able to handle rubygems plugins, this approach is maybe too much.

mterada1228 avatar Oct 25 '24 07:10 mterada1228

Thanks for checking the case that RDoc is installed as a default gem. It didn't work...

I agree that merging this after Ruby 3.4 is an option. We'll migrate RDoc to a bundled gem from a default gem for Ruby 3.5: https://bugs.ruby-lang.org/issues/20309 If RDoc is installed as a bundled gem, we don't need to care about the default gem case.

For merging this before Ruby 3.4, https://github.com/mterada1228/rdoc/pull/1 may work. (Sorry. I didn't test it yet. It's a concept implementation.) The followings are important parts:

  • https://github.com/mterada1228/rdoc/pull/1/files#diff-57670ce1156b5670b904c97f8a01d658a0b5a458ffe71f2fcc7a7eefd4254d2cR275-R277
    • Detect whether this RDoc is installed as a default gem or not. We can use lib/rubygems_plugin.rb to detect it.
  • https://github.com/mterada1228/rdoc/pull/1/files#diff-57670ce1156b5670b904c97f8a01d658a0b5a458ffe71f2fcc7a7eefd4254d2cR283-R298
    • Do nothing if RDoc is a normal gem or a bundled gem
    • Generate/remove if RDoc is a default gem as usual

It's not complex. If this works well, this approach may be an option. @mterada1228 Do you want to check whether this approach work with the following cases?

  • RDoc is installed as a normal gem: We did the check when we created this PR
  • RDoc is installed as a default gem: We did the check in https://github.com/ruby/rdoc/pull/1171#issuecomment-2437058355

kou avatar Oct 26 '24 22:10 kou

@kou

Thank you for your PR. I think that solution is so nice.

I want to try whether it may work or not, but the branch have to merge some fix in the master branch to try it. Can you merge the branch of add-rubygems-hook ?

mterada1228 avatar Oct 31 '24 23:10 mterada1228

@mterada1228 You want to try https://github.com/mterada1228/rdoc/pull/1 on https://github.com/ruby/rdoc/tree/master , right? If https://github.com/mterada1228/rdoc/pull/1 doesn't work as expected, we need to revert the change from https://github.com/ruby/rdoc/tree/master . So can we avoid the approach?

Can we try https://github.com/mterada1228/rdoc/pull/1 without merging it to https://github.com/ruby/rdoc/tree/master ? It seems that you merged the current master to https://github.com/mterada1228/rdoc/tree/add-rubygems-hook . So I rebased https://github.com/mterada1228/rdoc/pull/1 on the current https://github.com/mterada1228/rdoc/tree/add-rubygems-hook . Can we try https://github.com/mterada1228/rdoc/pull/1 by the following now?

$ git close https://github.com/kou/rdoc.git rdoc.kou
$ cd rdoc.kou
$ git switch add-rubygems-hook-default-gem
$ ...

kou avatar Nov 01 '24 22:11 kou

@kou

Sorry, My explanation was so bad... What I wanted is same as one you say.

Thank you for your operation. I'll test https://github.com/mterada1228/rdoc/pull/1 later.

mterada1228 avatar Nov 01 '24 23:11 mterada1228

@hsbt @kou

I merged @kou's proposal https://github.com/mterada1228/rdoc/pull/1. So, in case RDoc is installed as default gem, documents can be created.

Test

1. RDoc is installed as normal gem.

Setup

❯ rake install
rdoc 6.7.0 built to pkg/rdoc-6.7.0.gem.
rdoc (6.7.0) installed.

❯ gem list | grep rdoc
rdoc (6.7.0, default: 6.6.3.1)

Execution

❯ gem install pkg-config
Fetching pkg-config-1.5.7.gem
Successfully installed pkg-config-1.5.7
Parsing documentation for pkg-config-1.5.7
Installing ri documentation for pkg-config-1.5.7
Done installing documentation for pkg-config after 0 seconds
1 gem installed

2. RDoc is installed as default gem

Setup

# For backup
❯ mv $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc_bkup
❯ mv $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc.rb $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc.rb.bkup

# Copy sources to directory of default gems
❯ cp -r ./lib/rdoc $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc
❯ cp -r ./lib/rdoc.rb $(ruby -e 'print Gem::RUBYGEMS_DIR')/rdoc.rb

# Check
❯ gem list | grep rdoc
rdoc (default: 6.6.3.1)

Execution

❯ gem install pkg-config
Successfully installed pkg-config-1.5.7
Parsing documentation for pkg-config-1.5.7
Done installing documentation for pkg-config after 0 seconds
1 gem installed

mterada1228 avatar Nov 07 '24 23:11 mterada1228

This is ready again.

This should be worked when RDoc is installed as a default gem too. In the case, the existing integration logic in RubyGems (not RubyGems plugin logic) is used.

Are there any other concerns?

kou avatar Nov 10 '24 21:11 kou

@mterada1228 can you also update the description to reflect the latest changes? Thanks 😄

st0012 avatar Nov 13 '24 17:11 st0012

@st0012

@mterada1228 can you also update the description to reflect the latest changes? Thanks 😄

Thank you for your comment.

I changed a description of this PR. Following points are summary of changes.

1 Q3. When can we delete rubygems/lib/rubygems/rdoc.rb? -> I added a condition.

RDoc will be a bundled gem (not a default gem). (cf: https://bugs.ruby-lang.org/issues/20309)

2 Q4. Is it a breaking change that Rubygems creates documents with rubygems_plugin not RDoc::RubygemsHook? -> I changed description about the approach we took.

We change rdoc/rubygems_hook.rb and then make generation_hook a method that create documents when there is not rubygems_plugin.rb. (This is a change on RDoc.)

3 I added a chapter of Test for RDoc as default gem.

mterada1228 avatar Nov 15 '24 00:11 mterada1228

@st0012 @mterada1228 If you found another issue about this PR, we can fix this in the future like RDoc 6.9.1 and Ruby 3.4.1.

hsbt avatar Dec 13 '24 05:12 hsbt