Skip to content

Instantly share code, notes, and snippets.

@spudtrooper
Created March 8, 2011 04:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spudtrooper/859858 to your computer and use it in GitHub Desktop.
Save spudtrooper/859858 to your computer and use it in GitHub Desktop.
Repl for exploring github.com
#!/usr/bin/env ruby
# -*- ruby -*-
#
# Repl for exploring git. More here:
#
# http://www.jeffpalm.com/blog/archives/002194.html
#
require 'rubygems'
require 'hubruby'
class TreeNode
attr_accessor :name, :parent, :leaf
@kids = nil
def find_children
raise 'Implement find_children'
end
def children(force=false)
if force or not @kids
@kids = find_children || []
@kids.each do |node|
node.parent = self
end
end
@kids
end
end
class CmdNode < TreeNode
attr_reader :cmd
def initialize(obj,cmd)
@obj = obj
@cmd = cmd
@name = cmd.to_s
end
def find_children
@obj.send(@cmd).map {|v| tree_node_class(v.class).new v}
end
end
class NamedNode < TreeNode
attr_reader :obj
def initialize(obj)
@obj = obj
@name = @obj.name
end
def find_children
symbols.sort {|a,b| a.to_s <=> b.to_s}.map {|s| CmdNode.new @obj,s}
end
def symbols
raise 'Implement symbols()'
end
end
class UserNode < NamedNode
def initialize(o)
super(o)
end
def name
obj.login
end
def symbols
[:followers,
:following,
:repositories,
:watched]
end
end
class RepositoryNode < NamedNode
def initialize(o)
super(o)
end
def symbols
[:watchers,
:branches]
end
end
GITHUB_CLASSES2TREE_NODE_CLASSES = {
GitHub::User => UserNode,
GitHub::Repository => RepositoryNode,
}
def tree_node_class(cls)
GITHUB_CLASSES2TREE_NODE_CLASSES[cls]
end
class Repl
attr_reader :root,:commands
attr_accessor :cur
def initialize(root)
use root
@commands = [Ls,Cd,Pwd,Clone,Use,Help,Quit].map {|cls| cls.new self}
end
# ----------------------------------------------------------------------
# Commands
# ----------------------------------------------------------------------
class Cmd
attr_reader :repl,:name
def initialize(repl,name)
@repl = repl
@name = name
end
def help
raise 'Implement help()'
end
def exec(args)
raise 'Implement exec(string[])'
end
end
class Ls < Cmd
def initialize(repl)
super(repl,'ls')
end
def exec(args)
def ls_print(n,prefix=nil)
def pr(s,prefix)
if prefix
puts File.join prefix,s
else
puts s
end
end
puts if prefix
['.','..'].each do |s|
pr s,prefix
end
n.children.each do |node|
pr node.name,prefix
end
end
args = ['.'] if args.empty?
args.uniq!
args.each do |path|
n = repl.eval_path repl.cur,path
if n
prefix = args.length > 1 ? path : nil
ls_print n,prefix
end
end
end
def help
'List directory'
end
end
class Cd < Cmd
def initialize(repl)
super(repl,'cd')
end
def exec(args)
args = ['/'] if args.empty?
path = args[0]
n = repl.eval_path repl.cur,path
repl.cur = n if n
end
def help
'Change directory'
end
end
class Pwd < Cmd
def initialize(repl)
super(repl,'pwd')
end
def exec(args)
puts cwd
end
def help
'Print the current working directory'
end
end
class Help < Cmd
def initialize(repl)
super(repl,'help')
end
def help
'Print this message'
end
def exec(args)
puts 'Commands:'
repl.commands.sort {|a,b| a.name <=> b.name}.each do |cmd|
printf " %-10s %s\n",cmd.name,cmd.help
end
end
end
class Quit < Cmd
def initialize(repl)
super(repl,'quit')
end
def help
'Quit'
end
def exec(args)
exit 0
end
end
class Use < Cmd
def initialize(repl)
super(repl,'use')
end
def help
'Start exploring a certain github user'
end
def exec(args)
if args.empty?
STDERR.puts 'Missing required argument'
return
end
username = args[0]
user = nil
begin
user = GitHub.user username
rescue Exception => e
STDERR.puts "Could not create user for name: #{username}"
STDERR.puts e
end
if user
repl.use UserNode.new user
end
end
end
class Clone < Cmd
def initialize(repl)
super(repl,'clone')
end
def help
'Clone the current repository'
end
def exec(args)
args = ['.'] if args.empty?
args.each do |arg|
rel = repl.eval_path repl.cur,arg
if rel.kind_of? RepositoryNode
rep = rel.obj
url = "https://github.com/#{rep.owner}/#{rep.name}"
system 'git','clone',url
else
STDERR.puts 'Invalid repository: ' + arg
end
end
end
end
# ----------------------------------------------------------------------
# Main
# ----------------------------------------------------------------------
def main
strings2cmds = {}
commands.each do |cmd|
strings2cmds[cmd.name] = cmd
end
def loop(strings2cmds)
print cwd + '> '
orig_line = STDIN.readline.strip
line = orig_line.split(/ /)
return false if line.empty?
str = line.shift.downcase
return true if str == 'quit'
if not cur
if str != 'use' and str != 'help'
STDERR.puts 'You must set a user with \'use\' before anything else.'
return
end
end
args = line
cmd = strings2cmds[str]
if cmd
begin
cmd.exec args
rescue Exception => e
STDERR.puts 'Trouble executing: ' + orig_line
STDERR.puts e
end
else
STDERR.puts 'Unknown command: ' + str
end
return false
end
while true
break if loop strings2cmds
end
end
# ----------------------------------------------------------------------
# 'Private'
# ----------------------------------------------------------------------
# GitHub::User -> void
def use(user)
if user
@cur = user
@root = user
end
end
# TreeNode string -> TreeNode
def eval_path(node,path)
path = '.' if not path or path == ''
if path =~ /^\//
args = ['/'] + path.gsub(/^\/+/,'').split(/\//)
else
args = path.split /\//
end
trav = node
args.each do |arg|
case arg
when '.'
trav = trav
when '..'
trav = trav.parent
when '/'
trav = root
else
found = false
trav.children.each do |n|
if n.name == arg
trav = n
found = true
break
end
end
if not found
STDERR.puts 'Invalid path: ' + path
return nil
end
end
end
return trav
end
# void -> string
def cwd
path(root,cur).map {|n| n.name}.join('/')
end
# TreeNode string -> TreeNode
def node_relative_to(node,name)
if name == '.'
return node
elsif name == '..'
return node.parent
elsif name == '/'
return root
else
node.children.each do |n|
if name == n.name
return n
end
end
end
return nil
end
# TreeNode TreeNode -> TreeNode[]
def path(from,dest)
res = []
trav = dest
while trav
res.push trav
break if trav == from
trav = trav.parent
end
return res.reverse
end
end
# ----------------------------------------------------------------------
# Main entry
# ----------------------------------------------------------------------
def main(argv)
node = argv.empty? ? nil : UserNode.new(GitHub.user(argv[0]))
repl = Repl.new node
repl.main
end
main ARGV
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment