[Rubygems-developers] Design notes for RubyGems 2

Gavin Sinclair gsinclair at soyabean.com.au
Tue Jun 8 19:15:26 EDT 2004

On Wednesday, June 9, 2004, 4:15:52 AM, Curt wrote:

> Chad Fowler wrote:
>> On Wed, 9 Jun 2004, Gavin Sinclair wrote:
>> # The reality is that RubyGems is pretty simple.  I want an interface to
>> # the main thing in the system - the collection of gems - that is also
>> # simple.  Tell it want you want and you get it.  Obviously that needs
>> # to be codified somewhat.  But the current system is more like
>> # low-level scratching than high-level ease.
>> #
>> Agreed on the simplicity comment.  To me, the interface you're talking
>> about looks something like this:
>> Gem::Cache.from_installed_gems.each do |gemspec|
>>    Gem::DocManager.new(gemspec).generate_rdoc
>> end
>> There might be some things lacking, but I'd like to see them built based
>> on a concrete need for them.

> Why not:

>   repo = new Repository(LOCALHOST)
>   repo.each_installed_gem do |gem|
>     gem.generate_rdoc
>   end

> (Of course, the first two lines could be written as one line.)

I'll go you halfway.  Generating RDoc is a process, so I'd use
Tools::RDocGenerator to implement that process.  Now you don't
actually need a Gem for that; you need a directory (that's where turn
'rdoc' loose) and the gemspec (giving you RDoc options, etc.).

   # We don't create Repositories often; they are long-living objects
   # that know about all local and remote gems.  So we just use the
   # 'repo' object that's available to us.

   installed_gems = repo.gem_report(:installed)
    # -> [ GemResource<rake, 0.3.2, :installed, /usr/lib/ruby/gems/1.8>,
           GemResource<pqa,  0.6,   :installed, /usr/lib/ruby/gems/1.8>,
           ... ]

   installed_gems.each do |gem_resource|

In Chad's example, "Gem::DocManager.new(gemspec).generate_rdoc" has no
way of knowing *where* the gem is installed.  Not even Cache can tell

In Curt's example, a Gem object (if that's what 'gem' is) knows how to
generate RDoc, which is an inappropriate abstraction, I believe.
> A GUI gems browser is going to want to have a navigation tree control that
> can enumerate known repositories (local and remote) and the gems within each
> repository. Once enumerated and displayed, if the user selects an item in
> the GUI tree, it needs to know what that corresponds to.

In my design, as demonstrated above, the Repository gives you
GemResource objects.  Given a random GemResource object 'gr', you can:

  gr.name               -> rake
  gr.version            -> 0.3.2
  gr.installed?         -> false
  gr.cellared?          -> false
  gr.remote?            -> true
  gr.location_category  -> :remote
  gr.source             -> 'http://raa.ruby-lang.org/gems'

You want to retrieve this gem?

  gem = repo.get_gem(gr)

  gem.name              -> rake
  gem.version           -> 0.3.2
  gem.fullname          -> rake-0.3.2
  gem.spec              -> Gem::Specification<...>
  gem.data              -> "..." (raw data)
  gem.extract("README") -> "..."
  gem.extract_all do |path, data|
    # Hey, installation is pretty easy with this!

Now we can cellar it:

  Tools::Cellar.cellar(gem, base_dir)
    # Rough implementation:
    #   cellar_dir = Directories.cellar(base_dir)
    #   cellar_path = File.join(cellar_dir, gem.fullname + '.gem')
    #   File.write(cellar_path, gem.data)

Or install it:

  Tools::Installer.install(gem, base_dir, repo)
    # Rough implementation
    #     # Deal with dependencies.
    #   gem.spec.dependencies.each do |name, version_req|
    #     unless repo.installed?(name, version_req)
    #         # Communicate with app somehow to confirm installation.
    #       dep_gem = repo.get_gem(name, version_req)
    #         Installer.install(dep_gem, base_dir, repo)
    #       end
    #     end
    #   end
    #     # Now deal with the gem at hand.
    #   install_dir = Directories.installation(base_dir, gem)
    #   FileUtils.mkdir(install_dir)
    #   Dir.chdir(install_dir) do
    #     gem.extract_all do |path, data|
    #       FileUtils.mkdir_p(File.dirname(path))
    #       File.write(path, data)
    #     end
    #   end

Or if we have an _installed_ GemResource and we want to uninstall it:

  gr.installed?                -> true
  gr.source                    -> '/usr/lib/uby/gems/1.8'

    # Rough implementation.
    #   return unless gr.installed?
    #   base_dir = gr.source
    #   install_dir = Directories.installation(base_dir)
    #   FileUtils.rm_f(install_dir)

I hope that gives I decent idea of the API I had in mind.  All of that
is off the top of my head, so there could be inconsistencies or room
for improvement in there.


More information about the Rubygems-developers mailing list