-
Notifications
You must be signed in to change notification settings - Fork 341
/
gist.rb
286 lines (242 loc) · 7.08 KB
/
gist.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
require 'open-uri'
require 'net/https'
require 'optparse'
require 'json'
require 'base64'
require 'gist/manpage' unless defined?(Gist::Manpage)
require 'gist/version' unless defined?(Gist::Version)
# You can use this class from other scripts with the greatest of
# ease.
#
# >> Gist.read(gist_id)
# Returns the body of gist_id as a string.
#
# >> Gist.write(content)
# Creates a gist from the string `content`. Returns the URL of the
# new gist.
#
# >> Gist.copy(string)
# Copies string to the clipboard.
#
# >> Gist.browse(url)
# Opens URL in your default browser.
module Gist
extend self
GIST_URL = 'https://api.github.com/gists/%s'
CREATE_URL = 'https://api.github.com/gists'
if ENV['HTTPS_PROXY']
PROXY = URI(ENV['HTTPS_PROXY'])
elsif ENV['HTTP_PROXY']
PROXY = URI(ENV['HTTP_PROXY'])
else
PROXY = nil
end
PROXY_HOST = PROXY ? PROXY.host : nil
PROXY_PORT = PROXY ? PROXY.port : nil
# Parses command line arguments and does what needs to be done.
def execute(*args)
private_gist = defaults["private"]
gist_filename = nil
gist_extension = defaults["extension"]
browse_enabled = defaults["browse"]
description = 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('-p', '--[no-]private', 'Make the gist private') do |priv|
private_gist = priv
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('-o','--[no-]open', 'Open gist in browser') do |o|
browse_enabled = o
end
opts.on('-m', '--man', 'Print manual') do
Gist::Manpage.display("gist")
end
opts.on('-v', '--version', 'Print version') do
puts Gist::Version
exit
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
begin
opts.parse!(args)
if $stdin.tty? && args[0] != '-'
# Run without stdin.
if args.empty?
# No args, print help.
puts opts
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)
files.push({
:input => File.read(file),
:filename => file,
:extension => (File.extname(file) if file.include?('.'))
})
end
else
# Read from standard input.
input = $stdin.read
files = [{:input => input, :extension => gist_extension}]
end
url = write(files, private_gist, description)
browse(url) if browse_enabled
puts copy(url)
rescue => e
warn e
puts opts
end
end
# Create a gist on gist.github.com
def write(files, private_gist = false, description = nil)
url = URI.parse(CREATE_URL)
if PROXY_HOST
proxy = Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT)
http = proxy.new(url.host, url.port)
else
http = Net::HTTP.new(url.host, url.port)
end
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = ca_cert
req = Net::HTTP::Post.new(url.path)
req.body = JSON.generate(data(files, private_gist, description))
if auth_header = auth()
req.add_field('Authorization', auth_header)
response = http.start{|h| h.request(req) }
case response
when Net::HTTPCreated
JSON.parse(response.body)['html_url']
else
puts "Creating gist failed: #{response.code} #{response.message}"
exit(false)
end
end
# Given a gist id, returns its content.
def read(gist_id)
open(GIST_URL % gist_id).read
end
# Given a url, tries to open it in your browser.
# TODO: Linux
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}"`
end
end
# Tries to copy passed content to the clipboard.
def copy(content)
cmd = case true
when system("type pbcopy > /dev/null 2>&1")
:pbcopy
when system("type xclip > /dev/null 2>&1")
:xclip
when system("type putclip > /dev/null 2>&1")
:putclip
end
if cmd
IO.popen(cmd.to_s, 'r+') { |clip| clip.print content }
end
content
end
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)
i = 0
data = {}
data["files"] = files.map do |file|
i = i + 1
filename = file[:filename] ? file[:filename] : "gistfile#{i}"
{filename => {:content => file[:input]}}
end
data.merge!({ 'description' => description }) unless description.nil?
data.merge(private_gist ? { 'public' => false } : {})
end
# Returns a basic auth string of the user's GitHub credentials if set.
# http://github.com/guides/local-github-config
def auth
user = config("github.user")
password = config("github.password")
if user.to_s.empty? || token.to_s.empty?
nil
else
auth_str = Base64.encode64("#{user}:#{password}")
"Basic #{auth_str}"
end
end
# Returns default values based on settings in your gitconfig. See
# git-config(1) for more information.
#
# Settings applicable to gist.rb are:
#
# gist.private - boolean
# gist.extension - string
def defaults
extension = config("gist.extension")
return {
"private" => config("gist.private"),
"browse" => config("gist.browse"),
"extension" => extension
}
end
# Reads a config value using:
# => Environement: GITHUB_TOKEN, GITHUB_USER
# like vim gist plugin
# => git-config(1)
#
# return something useful or nil
def config(key)
env_key = ENV[key.upcase.gsub(/\./, '_')]
return env_key if env_key and not env_key.strip.empty?
str_to_bool `git config --global #{key}`.strip
end
# Parses a value that might appear in a .gitconfig file into
# something useful in a Ruby script.
def str_to_bool(str)
if str.size > 0 and str[0].chr == '!'
command = str[1, str.length]
value = `#{command}`
else
value = str
end
case value.downcase.strip
when "false", "0", "nil", "", "no", "off"
nil
when "true", "1", "yes", "on"
true
else
value
end
end
def ca_cert
cert_file = [
File.expand_path("../gist/cacert.pem", __FILE__),
"/tmp/gist_cacert.pem"
].find{|l| File.exist?(l) }
if cert_file
cert_file
else
File.open("/tmp/gist_cacert.pem", "w") do |f|
f.write(DATA.read.split("__CACERT__").last)
end
"/tmp/gist_cacert.pem"
end
end
end