How Rubygems commands works
I’m currently working on GSoC, and I have to Integrate functionality from gem-web into RubyGems gem CLI. So, we’re going to add a new CLI option to rubygems
. As my first task on it, my mentor Saroj Maharjan (@zoras) sent me some PRs (#1938 and #1944) to study, as well as the class Command
of rubygems
codebase. Here’s a bit of what I’ve learned:
Class Command
The first thing I noticed on the PRs was that every command inherits from the class Command
. This class works like an interface
to the children classes, that is, the children must override its methods to work properly. The comment at lib/rubygems/command.rb
sums this up:
Base class for all Gem commands. When creating a new gem command, define #initialize, #execute, #arguments, #defaults_str, #description and #usage (as appropriate).
Overriding
So we need to override these methods to create our new command. Let’s take a look at them:
initialize
: Initializes a new command, adding its name, a short description (that will be displayed ingem help commands
). The argumentdefaults
is a list of default arguments (that should be mirrored indefaults_str
). Here’s the generic implementation of this method:
# Gem::Command's initialize method
def initialize(command, summary=nil, defaults={})
@command = command
@summary = summary
@program_name = "gem #{command}"
@defaults = defaults
@options = defaults.dup
@option_groups = Hash.new { |h,k| h[k] = [] }
@deprecated_options = { command => {} }
@parser = nil
@when_invoked = nil
end
It’s possible to add new options to your command with add_option
. The initilize
method of our new command could look like this:
def initialize
super 'new_method', 'Does an awesome thing.'
add_option('--awesome', 'Adds awesomeness') do |value, options|
options[:awesome] = value
end
end
arguments
: Describes the arguments that a command takes. It should return a left-justified string, one argument per line. Here’s the code from the commandinstall
, for example:
def arguments
"GEMNAME name of gem to install"
end
usage
: Displays the usage for an individual gem command. The text “[options]” is automatically appended to the usage text. Take a look on the override of this method atGem::Commands::UpdateCommand
:
def usage
"#{program_name} GEMNAME [GEMNAME ...]"
end
defaults_str
: Defines the default arguments from the command. It’s really simple, take a look at this example:
# This method is similar to arguments, but displays the default values.
def defaults_str
--document --no-force
end
- description: This is just a long description for what the command does.
# You can add multiple lines as well
def description
'The `signout` command is used to sign out from all current sessions,'\
' allowing you to sign in using a different set of credentials.'
end
- execute: The generic method that you need to override to handle your command. Parsed option will fill
options
, while unparsed option will be left inoptions[:args]
. An override could look something like this:
# This will raise Gem::Exception if you don't override it
def execute
if options[:awesome]
add_awesomeness
end
do_other_stuff
end
Wrapping up
As you may have noticed, adding a new command to rubygems
is not rocket science. Adding tests to your new code is very important too!
BTW, the tests from rubygems
are written in Minitest, whereas gem-web
’s are in Rspec, so I’ll need to convert them before integrating gem-web
into rubygems
. I’ll probably talk about this in next week’s post. Stay tuned! I’m out!