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

Support for updating gists #62

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 72 additions & 34 deletions gist
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ require 'gist/version' unless defined?(Gist::Version)
module Gist
extend self

GIST_URL = 'https://gist.github.com/%s.txt'
GIST_URL = 'https://gist.github.com/%s'
CREATE_URL = 'https://gist.github.com/gists'
EDIT_URL = 'https://gist.github.com/gists/%s/edit'

if ENV['HTTPS_PROXY']
PROXY = URI(ENV['HTTPS_PROXY'])
Expand All @@ -107,24 +108,42 @@ module Gist

def execute(*args)
private_gist = defaults["private"]
anonymous_gist = false
gist_filename = nil
gist_extension = defaults["extension"]
browse_enabled = defaults["browse"]
copy = defaults["copy"]
read_mode = false
description = nil
update_to = nil

opts = OptionParser.new do |opts|
opts.banner = "Usage: gist [options] [filename or stdin] [filename] ...\n" +
"Filename '-' forces gist to read from stdin."

opts.on('-r', '--read', 'Get the gist for the given id') do
read_mode = true
end

opts.on('-p', '--[no-]private', 'Make the gist private') do |priv|
private_gist = priv
end

opts.on('-a', '--anonymous', 'Create an anonymous gist') do |anon|
anonymous_gist = anon
end

t_desc = 'Set syntax highlighting of the Gist by file extension'
opts.on('-t', '--type [EXTENSION]', t_desc) do |extension|
gist_extension = '.' + extension
end

opts.on('-d','--description DESCRIPTION', 'Set description of the new gist') do |d|
description = d
end
opts.on('-u', '--update GIST_ID', 'Update an already existing gist') do |i|
update_to = i
end

opts.on('-o','--[no-]open', 'Open gist in browser') do |o|
browse_enabled = o
end
Expand All @@ -142,28 +161,33 @@ module Gist
puts opts
exit
end

opts.on('-c', '--[no-]copy', 'Copy gist URL to clipboard automatically') do |c|
copy = c
end
end

opts.parse!(args)

begin

opts.parse!(args)

if $stdin.tty? && args[0] != '-'

if args.empty?
puts opts
exit
end

if read_mode
args.each do |gist_id|
next if gist_id =~ /^\_/
Gist.read(gist_id)
end
exit
end

files = args.inject([]) do |files, file|
abort "Can't find #{file}" unless File.exists?(file)

files.push({
:input => File.read(file),
:filename => File.basename(file),
:filename => file,
:extension => (File.extname(file) if file.include?('.'))
})
end
Expand All @@ -173,18 +197,18 @@ module Gist
files = [{:input => input, :extension => gist_extension}]
end

url = write(files, private_gist)
url = write(files, private_gist, description, anonymous_gist, update_to)
browse(url) if browse_enabled
copy(url) if copy
$stdout.tty? ? puts(url) : print(url)
puts copy(url)
rescue => e
warn e
puts opts
end
end

def write(files, private_gist = false)
url = URI.parse(CREATE_URL)
def write(files, private_gist = false, description = nil, anonymous_gist = false, update_to = nil)
update_id = update_to ? real_gistid(update_to) : nil
url = URI.parse(CREATE_URL + (update_id ? "/#{update_id}":""))

if PROXY_HOST
proxy = Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT)
Expand All @@ -198,7 +222,7 @@ module Gist
http.ca_file = ca_cert

req = Net::HTTP::Post.new(url.path)
req.form_data = data(files, private_gist)
req.form_data = data(files, private_gist, description, anonymous_gist, update_id ? update_to : nil)

response = http.start{|h| h.request(req) }
case response
Expand All @@ -211,12 +235,18 @@ module Gist
end

def read(gist_id)
open(GIST_URL % gist_id).read
response = open((GIST_URL % gist_id) + "?login=#{auth[:login]}&token=#{auth[:token]}").read
matches = response.scan(/href="\/(raw\S+)"/).flatten
matches.each do |raw|
puts open(GIST_URL % raw).read()
end
end

def browse(url)
if RUBY_PLATFORM =~ /darwin/
`open #{url}`
elsif RUBY_PLATFORM =~ /linux/
`#{ENV['BROWSER']} #{url}`
elsif ENV['OS'] == 'Windows_NT' or
RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw|wince/i
`start "" "#{url}"`
Expand All @@ -241,15 +271,29 @@ module Gist
end

private
def data(files, private_gist)
def data(files, private_gist, description, anonymous_gist = false, update_to)
data = {}
files.each do |file|
i = data.size + 1
data["file_ext[gistfile#{i}]"] = file[:extension] ? file[:extension] : '.txt'
data["file_name[gistfile#{i}]"] = file[:filename]
data["file_contents[gistfile#{i}]"] = file[:input]
end
data.merge(private_gist ? { 'action_button' => 'private' } : {}).merge(auth)
data.merge!({ 'description' => description }) unless description.nil?
data.merge!(private_gist ? { 'action_button' => 'private' } : {})
data.merge!(!update_to.nil? ? {'_method' => 'put'} : {})
data.merge!(anonymous_gist ? {} : auth)
end

def real_gistid(gist_id)
response = open((EDIT_URL % gist_id) + "?login=#{auth[:login]}&token=#{auth[:token]}").read
if response =~ /This\syear/
return nil
end
match = /<form action="\/gists\/(\d+)"/.match(response)
if match
return match[1].to_i
end
end

def auth
Expand All @@ -265,27 +309,17 @@ private

def defaults
extension = config("gist.extension")
extension = nil if extension && extension.empty?

copy = config("gist.copy")
if copy.nil?
copy = true
else
# match optparse boolean true states
copy = copy =~ /^(true)|(on)|(\+)/
end

return {
"private" => config("gist.private"),
"browse" => config("gist.browse"),
"extension" => extension,
"copy" => copy,
"extension" => extension
}
end

def config(key)
env_key = ENV[key.upcase.gsub(/\./, '_')]
return env_key if env_key and not env_key.empty?
return env_key if env_key and not env_key.strip.empty?

str_to_bool `git config --global #{key}`.strip
end
Expand Down Expand Up @@ -330,7 +364,7 @@ __END__
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "GIST" "1" "April 2011" "GITHUB" "Gist Manual"
.TH "GIST" "1" "June 2011" "GITHUB" "Gist Manual"
.
.SH "NAME"
\fBgist\fR \- gist on the command line
Expand All @@ -348,7 +382,7 @@ If standard input is supplied, it will be used as the content of the new gist\.
Once your gist is successfully created, the URL will be copied to your clipboard\. If you are on OS X, \fBgist\fR will open the gist in your browser, too\.
.
.SH "OPTIONS"
\fBgist\fR\'s default mode of operation is to read content from standard input and create a public, text gist from it, tied to your GitHub account if you user and token are provided (see \fBCONFIGURATION\fR)\.
\fBgist\fR\'s default mode of operation is to read content from standard input and create a public, text gist without description from it, tied to your GitHub account if you user and token are provided (see \fBCONFIGURATION\fR)\.
.
.P
These options can be used to change this behavior:
Expand All @@ -359,7 +393,11 @@ Create a private gist instead of a public gist\.
.
.TP
\fB\-t\fR, \fB\-\-type\fR
Set the file extension explicitly\. Passing a type of \fBrb\fR ensure the gist is created as a Ruby file\.
Set the file extension explicitly\. Passing a type of \fBrb\fR ensures the gist is created as a Ruby file\.
.
.TP
\fB\-d\fR, \fB\-\-description\fR
Set a description\.
.
.TP
\fB\-o\fR, \fB\-\-[no\-]open\fR
Expand All @@ -380,7 +418,7 @@ Display this man page\.
There are two ways to set GitHub user and token info:
.
.IP "\(bu" 4
Using env vars GITHUB_USER and GITHUB_TOKEN
Using environment vars GITHUB_USER and GITHUB_TOKEN
.
.IP
$ export GITHUB_USER=johndoe
Expand Down
61 changes: 53 additions & 8 deletions lib/gist.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
module Gist
extend self

GIST_URL = 'https://gist.github.com/%s.txt'
GIST_URL = 'https://gist.github.com/%s'
CREATE_URL = 'https://gist.github.com/gists'
EDIT_URL = 'https://gist.github.com/gists/%s/edit'

if ENV['HTTPS_PROXY']
PROXY = URI(ENV['HTTPS_PROXY'])
Expand All @@ -39,19 +40,30 @@ module Gist
# Parses command line arguments and does what needs to be done.
def execute(*args)
private_gist = defaults["private"]
anonymous_gist = false
gist_filename = nil
gist_extension = defaults["extension"]
browse_enabled = defaults["browse"]
read_mode = false
description = nil
update_to = nil

opts = OptionParser.new do |opts|
opts.banner = "Usage: gist [options] [filename or stdin] [filename] ...\n" +
"Filename '-' forces gist to read from stdin."

opts.on('-r', '--read', 'Get the gist for the given id') do
read_mode = true
end

opts.on('-p', '--[no-]private', 'Make the gist private') do |priv|
private_gist = priv
end

opts.on('-a', '--anonymous', 'Create an anonymous gist') do |anon|
anonymous_gist = anon
end

t_desc = 'Set syntax highlighting of the Gist by file extension'
opts.on('-t', '--type [EXTENSION]', t_desc) do |extension|
gist_extension = '.' + extension
Expand All @@ -60,6 +72,9 @@ def execute(*args)
opts.on('-d','--description DESCRIPTION', 'Set description of the new gist') do |d|
description = d
end
opts.on('-u', '--update GIST_ID', 'Update an already existing gist') do |i|
update_to = i
end

opts.on('-o','--[no-]open', 'Open gist in browser') do |o|
browse_enabled = o
Expand Down Expand Up @@ -93,6 +108,14 @@ def execute(*args)
exit
end

if read_mode
args.each do |gist_id|
next if gist_id =~ /^\_/
Gist.read(gist_id)
end
exit
end

files = args.inject([]) do |files, file|
# Check if arg is a file. If so, grab the content.
abort "Can't find #{file}" unless File.exists?(file)
Expand All @@ -110,7 +133,7 @@ def execute(*args)
files = [{:input => input, :extension => gist_extension}]
end

url = write(files, private_gist, description)
url = write(files, private_gist, description, anonymous_gist, update_to)
browse(url) if browse_enabled
puts copy(url)
rescue => e
Expand All @@ -120,8 +143,9 @@ def execute(*args)
end

# Create a gist on gist.github.com
def write(files, private_gist = false, description = nil)
url = URI.parse(CREATE_URL)
def write(files, private_gist = false, description = nil, anonymous_gist = false, update_to = nil)
update_id = update_to ? real_gistid(update_to) : nil
url = URI.parse(CREATE_URL + (update_id ? "/#{update_id}":""))

if PROXY_HOST
proxy = Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT)
Expand All @@ -135,7 +159,7 @@ def write(files, private_gist = false, description = nil)
http.ca_file = ca_cert

req = Net::HTTP::Post.new(url.path)
req.form_data = data(files, private_gist, description)
req.form_data = data(files, private_gist, description, anonymous_gist, update_id ? update_to : nil)

response = http.start{|h| h.request(req) }
case response
Expand All @@ -149,7 +173,11 @@ def write(files, private_gist = false, description = nil)

# Given a gist id, returns its content.
def read(gist_id)
open(GIST_URL % gist_id).read
response = open((GIST_URL % gist_id) + "?login=#{auth[:login]}&token=#{auth[:token]}").read
matches = response.scan(/href="\/(raw\S+)"/).flatten
matches.each do |raw|
puts open(GIST_URL % raw).read()
end
end

# Given a url, tries to open it in your browser.
Expand Down Expand Up @@ -186,7 +214,7 @@ def copy(content)
private
# Give an array of file information and private boolean, returns
# an appropriate payload for POSTing to gist.github.com
def data(files, private_gist, description)
def data(files, private_gist, description, anonymous_gist = false, update_to)
data = {}
files.each do |file|
i = data.size + 1
Expand All @@ -195,7 +223,24 @@ def data(files, private_gist, description)
data["file_contents[gistfile#{i}]"] = file[:input]
end
data.merge!({ 'description' => description }) unless description.nil?
data.merge(private_gist ? { 'action_button' => 'private' } : {}).merge(auth)
data.merge!(private_gist ? { 'action_button' => 'private' } : {})
data.merge!(!update_to.nil? ? {'_method' => 'put'} : {})
data.merge!(anonymous_gist ? {} : auth)
end

# private gists have two ids, and we need the one that's only used in the
# edit page of a gist
def real_gistid(gist_id)
response = open((EDIT_URL % gist_id) + "?login=#{auth[:login]}&token=#{auth[:token]}").read
# "This year" only appears on the "all gists" page, which we get if we try
# to open a not existing gist or a gist that isn't ours.
if response =~ /This\syear/
return nil
end
match = /<form action="\/gists\/(\d+)"/.match(response)
if match
return match[1].to_i
end
end

# Returns a hash of the user's GitHub credentials if set.
Expand Down
Loading