├── Rakefile ├── Gemfile ├── .gitignore ├── lib ├── rubygems_plugin.rb └── rubygems │ ├── commands │ ├── open_command.rb │ ├── browse_command.rb │ ├── edit_command.rb │ └── clone_command.rb │ └── browse │ └── command.rb ├── gem-browse.gemspec ├── MIT-LICENSE └── README.markdown /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | -------------------------------------------------------------------------------- /lib/rubygems_plugin.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems/command_manager' 2 | 3 | Gem::CommandManager.instance.register_command :edit 4 | Gem::CommandManager.instance.register_command :open 5 | Gem::CommandManager.instance.register_command :clone 6 | Gem::CommandManager.instance.register_command :browse 7 | -------------------------------------------------------------------------------- /lib/rubygems/commands/open_command.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems/browse/command' 2 | require 'rubygems/version_option' 3 | 4 | class Gem::Commands::OpenCommand < Gem::Browse::Command 5 | include Gem::VersionOption 6 | 7 | def initialize 8 | super 'open', 'Open a gem in your editor', :version => '>= 0' 9 | add_editor_option 10 | add_version_option 11 | end 12 | 13 | def execute 14 | name = get_one_gem_name 15 | gemspec = find_by_name(name, options[:version]) 16 | Dir.chdir(gemspec.full_gem_path) do 17 | edit(gemspec.full_gem_path) 18 | end 19 | rescue Gem::LoadError 20 | alert_error "Could not find a valid gem #{name} (#{options[:version]})" 21 | terminate_interaction 1 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /lib/rubygems/commands/browse_command.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems/browse/command' 2 | 3 | class Gem::Commands::BrowseCommand < Gem::Browse::Command 4 | 5 | def initialize 6 | super 'open', "Open a gem's homepage in your web browser" 7 | end 8 | 9 | def execute 10 | name = get_one_gem_name 11 | homepage = 12 | begin 13 | find_by_name(name).homepage 14 | rescue Gem::LoadError 15 | get_json(name)[/"homepage_uri":\s*"([^"]*)"/, 1] 16 | end 17 | homepage = "https://rubygems.org/gems/#{name}" if homepage.to_s.empty? 18 | unless system('git', 'web--browse', homepage) 19 | alert_error('Error starting web browser (using git web--browse).') 20 | terminate_interaction 1 21 | end 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /gem-browse.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "gem-browse" 6 | s.version = "1.0.1" 7 | s.authors = ["Tim Pope"] 8 | s.email = ["code@tpop"+'e.net'] 9 | s.homepage = "https://github.com/tpope/gem-browse" 10 | s.summary = %q{gem edit, gem open, gem clone, gem browse} 11 | s.description = <<-EOS 12 | gem edit: edit a library file you can require. 13 | gem open: edit a gem by name. 14 | gem clone: clone a gem from GitHub. 15 | gem browse: open a gem's homepage in your browser. 16 | EOS 17 | s.license = 'MIT' 18 | 19 | s.files = `git ls-files`.split("\n") 20 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 21 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 22 | s.require_paths = ["lib"] 23 | 24 | s.add_development_dependency('rake', '~> 13.0.1') 25 | end 26 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Tim Pope 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/rubygems/browse/command.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems/command' 2 | 3 | module Gem::Browse 4 | class Command < Gem::Command 5 | 6 | def add_editor_option 7 | add_option('-e', '--editor EDITOR', 'Specify editor to invoke') do |editor, options| 8 | options[:editor] = editor 9 | end 10 | end 11 | 12 | def editor 13 | options[:editor] || 14 | ENV['GEM_EDITOR'] || 15 | ENV['VISUAL'] || 16 | ENV['EDITOR'] || 17 | 'vi' 18 | end 19 | 20 | def edit(*args) 21 | unless system(*editor.split(/\s+/) + args) 22 | alert_error "Problem with editor #{editor}" 23 | terminate_interaction 1 24 | end 25 | end 26 | 27 | def find_by_name(*args) 28 | if Gem::Specification.respond_to?(:find_by_name) 29 | Gem::Specification.find_by_name(*args) 30 | else 31 | Gem.source_index.find_name(*args).last or raise Gem::LoadError 32 | end 33 | end 34 | 35 | def get_json(name) 36 | require 'open-uri' 37 | begin 38 | URI.open("https://rubygems.org/api/v1/gems/#{name}.json").read 39 | rescue OpenURI::HTTPError 40 | alert_error "Cannot retrieve gem information for #{name} from rubygems.org." 41 | terminate_interaction 1 42 | end 43 | end 44 | 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/rubygems/commands/edit_command.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems/browse/command' 2 | 3 | class Gem::Commands::EditCommand < Gem::Browse::Command 4 | 5 | def description 6 | <<-EOF 7 | The edit command looks through the require paths of each gem for the 8 | specified library file and loads said file in your editor. If no file is 9 | found, $LOAD_PATH is searched before giving up. 10 | EOF 11 | end 12 | 13 | def initialize 14 | super 'edit', 'Edit a library file you can require' 15 | add_editor_option 16 | end 17 | 18 | def execute 19 | unless Gem::Specification.respond_to?(:find_by_path) 20 | $stderr.puts "RubyGems >= 1.8.0 is required to use gem edit." 21 | terminate_interaction 1 22 | end 23 | 24 | specs = options[:args].map do |lib| 25 | [lib, Gem::Specification.find_by_path(lib)] 26 | end 27 | 28 | paths = specs.map do |(lib, spec)| 29 | if spec 30 | find(lib, spec.require_paths.map {|p| File.join(spec.full_gem_path, p)}) 31 | else 32 | find(lib, $LOAD_PATH) 33 | end 34 | end.compact 35 | 36 | gemspecs = specs.map {|s| s.last}.compact.uniq 37 | dir = if gemspecs.size == 1 38 | gemspecs.first.full_gem_path 39 | else 40 | File.join(Gem.dir, 'gems') 41 | end 42 | 43 | unless paths.empty? 44 | Dir.chdir(dir) do 45 | edit(*paths) 46 | end 47 | end 48 | end 49 | 50 | # Adapted from Gem::Commands::WhichCommand 51 | def find(lib, paths) 52 | paths.each do |path| 53 | Gem.suffixes.each do |ext| 54 | full_path = File.join(path, "#{lib}#{ext}") 55 | if File.exist?(full_path) && !File.directory?(full_path) 56 | return full_path 57 | end 58 | end 59 | end 60 | 61 | alert_warning("Could not find #{lib.inspect} in any require path") 62 | end 63 | 64 | end 65 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | gem-browse 2 | ========== 3 | 4 | Open a library file you can require in your editor. That's it. 5 | 6 | gem edit active_support/all 7 | gem edit rake/task thor/task 8 | gem edit -e mvim fileutils 9 | 10 | Actually that's not it. You can also open a gem by name. 11 | 12 | gem open bundler 13 | 14 | Your editor's current working directory will be the root of the gem. 15 | 16 | I almost forgot. You can also clone a gem from GitHub. 17 | 18 | gem clone rails 19 | gem clone -d ~/src capybara 20 | 21 | And you can tell it to open the gem in your editor afterwards. 22 | 23 | gem clone -o rack 24 | gem clone -oe mvim -d /tmp gem-browse 25 | 26 | This one doesn't work if the neither the homepage nor the source code 27 | URL point back at GitHub. 28 | 29 | That's really it. I mean other than the command that lets you open a 30 | gem's homepage in your browser. You know, the command this gem is named 31 | after. 32 | 33 | gem browse sprockets 34 | 35 | Installation 36 | ------------ 37 | 38 | RubyGems 1.8 is required to use `gem edit`, but the other commands will 39 | work on any version that supports RubyGems plugins. 40 | 41 | gem install gem-browse 42 | 43 | If you're using RVM, you can put it in the global gemset (relax, it has 44 | no dependencies): 45 | 46 | echo gem-browse >> ~/.rvm/gemsets/global.gems 47 | rvm @global do gem install gem-browse 48 | 49 | Protip: Install [gem-ctags](https://github.com/tpope/gem-ctags) to 50 | automatically invoke [Ctags](http://ctags.sourceforge.net/) on gems as 51 | they are installed. 52 | 53 | Contributing 54 | ------------ 55 | 56 | Don't submit a pull request with [an ugly commit 57 | message](http://stopwritingramblingcommitmessages.com) or I will ignore 58 | your patch until I have the energy to politely explain my zero tolerance 59 | policy. 60 | 61 | License 62 | ------- 63 | 64 | Copyright (c) Tim Pope. MIT License. 65 | -------------------------------------------------------------------------------- /lib/rubygems/commands/clone_command.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems/browse/command' 2 | 3 | class Gem::Commands::CloneCommand < Gem::Browse::Command 4 | 5 | def description 6 | <<-EOF 7 | The clone command performs a Git clone of a gem's upstream repository. 8 | It looks for this repository by checking the homepage field of the gemspec 9 | and the source code URL field at rubygems.org. Currently, only GitHub 10 | repositories are recognized. 11 | EOF 12 | end 13 | 14 | def initialize 15 | super 'clone', "Clone a gem's source from GitHub" 16 | add_editor_option 17 | add_option('-o', '--[no-]open', 'Open in editor afterwards') do |open, options| 18 | options[:open] = open 19 | end 20 | add_option('-d', '--directory DIR', 'Parent directory to clone into') do |directory, options| 21 | options[:directory] = directory 22 | end 23 | end 24 | 25 | def execute 26 | name = get_one_gem_name 27 | json = nil 28 | 29 | homepage = 30 | begin 31 | find_by_name(name).homepage 32 | rescue Gem::LoadError 33 | json = get_json(name) 34 | json[/"homepage_uri":\s*"([^"]*)"/, 1] 35 | end 36 | 37 | unless url = repo(homepage) 38 | json ||= get_json(name) 39 | unless url = repo(json[/"source_code_uri":\s*"([^"]*)"/, 1]) 40 | alert_error "Could not find a GitHub URL for #{name}" 41 | terminate_interaction 1 42 | end 43 | end 44 | 45 | target = File.join(*[options[:directory], url[/([^\/]*)\.git$/, 1]].compact) 46 | unless system('git', 'clone', url, target) 47 | alert_error "Failed to clone #{url}" 48 | terminate_interaction 1 49 | end 50 | 51 | if options[:open] 52 | Dir.chdir(target) do 53 | edit('.') 54 | end 55 | end 56 | end 57 | 58 | def repo(url) 59 | case url.to_s 60 | when %r{://(?:wiki\.)?github\.com/([^/]*/[^/]*)} 61 | "git://github.com/#$1.git" 62 | when %r{://([^./]*)\.github\.com/([^/]*)} 63 | "git://github.com/#$1/#$2.git" 64 | end 65 | end 66 | 67 | end 68 | --------------------------------------------------------------------------------