Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it loose coupling between RubyGems and RDoc #1171

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

mterada1228
Copy link
Contributor

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.

\### 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.
@@ -0,0 +1,277 @@
# frozen_string_literal: true
Copy link
Contributor Author

@mterada1228 mterada1228 Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost changes of this file are what it is copied from lib/doc/rubygems_hook.rb except from logic for using rubygems_plugins.

diff
❯ git diff ef92423ef1b6492bcec7d94971d63b87f5adf6c5:lib/rubygems_plugin.rb upstream/master:lib/rdoc/rubygems_hook.rb
diff --git a/lib/rubygems_plugin.rb b/lib/rdoc/rubygems_hook.rb
index e2a4af76..3160072e 100644
--- a/lib/rubygems_plugin.rb
+++ b/lib/rdoc/rubygems_hook.rb
@@ -1,28 +1,15 @@
 # frozen_string_literal: true
-
 require 'rubygems/user_interaction'
 require 'fileutils'
-
-require_relative 'rdoc'
+require_relative '../rdoc'

 ##
 # Gem::RDoc provides methods to generate RDoc and ri data for installed gems
 # upon gem installation.
 #
 # This file is automatically required by RubyGems 1.9 and newer.
-#
-# Difference between RubygemsHook and RubyGemsHook.
-#   - RubygemsHook is executed from RubyGems.
-#   - RubyGemsHook is executed from rubygems_plugin.
-#
-# The reason why we use two classes.
-# If there are classes named 'RubygemsHook',
-# conflicts of name may happen.
-#
-# We can add methods to existing RubygemsHook,
-# but we didn't adopt it because of less mentenability and create new class 'RubyGemsHook'.
-#
-class RDoc::RubyGemsHook
+
+class RDoc::RubygemsHook

   include Gem::UserInteraction
   extend  Gem::UserInteraction
@@ -58,7 +45,7 @@ class RDoc::RubyGemsHook
   # Post installs hook that generates documentation for each specification in
   # +specs+

-  def self.generate installer, specs
+  def self.generation_hook installer, specs
     start = Time.now
     types = installer.document

@@ -77,10 +64,6 @@ class RDoc::RubyGemsHook
     say "Done installing documentation for #{names} after #{duration} seconds"
   end

-  def self.remove uninstaller
-    new(uninstaller.spec).remove
-  end
-
   ##
   # Loads the RDoc generator

@@ -263,15 +246,3 @@ class RDoc::RubyGemsHook

@mterada1228 mterada1228 marked this pull request as draft September 5, 2024 23:52
@mterada1228 mterada1228 marked this pull request as ready for review September 7, 2024 05:49
@kou
Copy link
Member

kou commented Sep 11, 2024

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

@st0012
Copy link
Member

st0012 commented Sep 12, 2024

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?

@mterada1228
Copy link
Contributor Author

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.

@kou
Copy link
Member

kou commented Sep 13, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants