Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 510 lines (439 sloc) 14.81 kB
f68ce33 @locks fix for 1.8.7
locks authored
1 require 'net/https'
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
2 require 'cgi'
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
3 require 'uri'
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
4
cf2b261 @ConradIrwin Remove JSON dependency [Fixes #152]
ConradIrwin authored
5 begin
6 require 'json'
7 rescue LoadError
8 require File.join File.dirname(File.dirname(__FILE__)), 'vendor', 'json.rb'
9 end
10
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
11 # It just gists.
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
12 module Gist
652a089 @rking use extend(self) instead of module_function
rking authored
13 extend self
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
14
1a5e2ca @ConradIrwin v4.4.0
ConradIrwin authored
15 VERSION = '4.4.0'
a6d40fa @ConradIrwin Make error handling a bit more friendly [Issue #4]
ConradIrwin authored
16
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
17 # A list of clipboard commands with copy and paste support.
18 CLIPBOARD_COMMANDS = {
19 'xclip' => 'xclip -o',
63ab52b @ryanmjacobs Align arrow thingies.
ryanmjacobs authored
20 'xsel -i' => 'xsel -o',
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
21 'pbcopy' => 'pbpaste',
22 'putclip' => 'getclip'
23 }
37f00cb @rking Add clipboard support and tests
rking authored
24
5127b8f @kui Make code more testable
kui authored
25 GITHUB_API_URL = URI("https://api.github.com/")
26 GIT_IO_URL = URI("http://git.io")
27
28 GITHUB_BASE_PATH = ""
29 GHE_BASE_PATH = "/api/v3"
30
fa383c1 @kui Use GITHUB_URL instead of GHE_URL
kui authored
31 URL_ENV_NAME = "GITHUB_URL"
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
32
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
33 USER_AGENT = "gist/#{VERSION} (Net::HTTP, #{RUBY_DESCRIPTION})"
6a0943d @ConradIrwin Include a User-Agent [Fixes #34]
ConradIrwin authored
34
a6d40fa @ConradIrwin Make error handling a bit more friendly [Issue #4]
ConradIrwin authored
35 # Exception tag for errors raised while gisting.
1ab99bc @ConradIrwin All 'raise Jist::Error'
ConradIrwin authored
36 module Error;
37 def self.exception(*args)
38 RuntimeError.new(*args).extend(self)
39 end
40 end
776427f @rking Spiffier Errors (internally + externally)
rking authored
41 class ClipboardError < RuntimeError; include Error end
672bd05 @ConradIrwin Version 0.2!
ConradIrwin authored
42
b5b7ae5 @jasonkarns move AuthTokenFile into gist.rb
jasonkarns authored
43 # helper module for authentication token actions
44 module AuthTokenFile
45 def self.filename
46 if ENV.key?(URL_ENV_NAME)
47 File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/[^a-z.]/, '')}"
48 else
49 File.expand_path "~/.gist"
50 end
51 end
52
53 def self.read
54 File.read(filename).chomp
55 end
56
57 def self.write(token)
58 File.open(filename, 'w', 0600) do |f|
59 f.write token
60 end
61 end
62 end
63
90fb58f @ConradIrwin Tidy up output a little
ConradIrwin authored
64 # auth token for authentication
f4c3148 @pravj add method to handle access token internally
pravj authored
65 #
66 # @return [String] string value of access token or `nil`, if not found
67 def auth_token
59bf572 @jasonkarns AuthTokenFile needn't be a class (no state). just functions
jasonkarns authored
68 @token ||= AuthTokenFile.read rescue nil
f4c3148 @pravj add method to handle access token internally
pravj authored
69 end
70
d7fe3d0 @ConradIrwin yarderize
ConradIrwin authored
71 # Upload a gist to https://gist.github.com
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
72 #
d7fe3d0 @ConradIrwin yarderize
ConradIrwin authored
73 # @param [String] content the code you'd like to gist
4a98996 @ConradIrwin Try to clean up output stuff
ConradIrwin authored
74 # @param [Hash] options more detailed options, see
75 # the documentation for {multi_gist}
d7fe3d0 @ConradIrwin yarderize
ConradIrwin authored
76 #
77 # @see http://developer.github.com/v3/gists/
8464f65 @ConradIrwin Fix some yard problems
ConradIrwin authored
78 def gist(content, options = {})
e412601 @ConradIrwin Allow gisting multiple files at the same time
ConradIrwin authored
79 filename = options[:filename] || "a.rb"
80 multi_gist({filename => content}, options)
81 end
82
83 # Upload a gist to https://gist.github.com
84 #
8464f65 @ConradIrwin Fix some yard problems
ConradIrwin authored
85 # @param [Hash] files the code you'd like to gist: filename => content
e412601 @ConradIrwin Allow gisting multiple files at the same time
ConradIrwin authored
86 # @param [Hash] options more detailed options
87 #
88 # @option options [String] :description the description
89 # @option options [Boolean] :public (false) is this gist public
81a8244 @ConradIrwin Improve docs for -a
ConradIrwin authored
90 # @option options [Boolean] :anonymous (false) is this gist anonymous
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
91 # @option options [String] :access_token (`File.read("~/.gist")`) The OAuth2 access token.
e412601 @ConradIrwin Allow gisting multiple files at the same time
ConradIrwin authored
92 # @option options [String] :update the URL or id of a gist to update
37f00cb @rking Add clipboard support and tests
rking authored
93 # @option options [Boolean] :copy (false) Copy resulting URL to clipboard, if successful.
472ba40 @ConradIrwin Add --open/-o as well
ConradIrwin authored
94 # @option options [Boolean] :open (false) Open the resulting URL in a browser.
4a98996 @ConradIrwin Try to clean up output stuff
ConradIrwin authored
95 # @option options [Symbol] :output (:all) The type of return value you'd like:
96 # :html_url gives a String containing the url to the gist in a browser
97 # :short_url gives a String contianing a git.io url that redirects to html_url
98 # :javascript gives a String containing a script tag suitable for embedding the gist
99 # :all gives a Hash containing the parsed json response from the server
e412601 @ConradIrwin Allow gisting multiple files at the same time
ConradIrwin authored
100 #
4a98996 @ConradIrwin Try to clean up output stuff
ConradIrwin authored
101 # @return [String, Hash] the return value as configured by options[:output]
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
102 # @raise [Gist::Error] if something went wrong
e412601 @ConradIrwin Allow gisting multiple files at the same time
ConradIrwin authored
103 #
104 # @see http://developer.github.com/v3/gists/
105 def multi_gist(files, options={})
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
106 json = {}
107
108 json[:description] = options[:description] if options[:description]
109 json[:public] = !!options[:public]
e412601 @ConradIrwin Allow gisting multiple files at the same time
ConradIrwin authored
110 json[:files] = {}
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
111
e412601 @ConradIrwin Allow gisting multiple files at the same time
ConradIrwin authored
112 files.each_pair do |(name, content)|
a6d40fa @ConradIrwin Make error handling a bit more friendly [Issue #4]
ConradIrwin authored
113 raise "Cannot gist empty files" if content.to_s.strip == ""
e412601 @ConradIrwin Allow gisting multiple files at the same time
ConradIrwin authored
114 json[:files][File.basename(name)] = {:content => content}
115 end
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
116
425182d @ConradIrwin Allow updating an existing gist
ConradIrwin authored
117 existing_gist = options[:update].to_s.split("/").last
03afea3 @ainformatico Add anonymous gist support
ainformatico authored
118 if options[:anonymous]
119 access_token = nil
120 else
f4c3148 @pravj add method to handle access token internally
pravj authored
121 access_token = (options[:access_token] || auth_token())
03afea3 @ainformatico Add anonymous gist support
ainformatico authored
122 end
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
123
5127b8f @kui Make code more testable
kui authored
124 url = "#{base_path}/gists"
425182d @ConradIrwin Allow updating an existing gist
ConradIrwin authored
125 url << "/" << CGI.escape(existing_gist) if existing_gist.to_s != ''
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
126 url << "?access_token=" << CGI.escape(access_token) if access_token.to_s != ''
127
128 request = Net::HTTP::Post.new(url)
4891d4a @ConradIrwin Windows support...
ConradIrwin authored
129 request.body = JSON.dump(json)
75a5731 @ConradIrwin Set 'Content-Type: application/json' [Fixes #7]
ConradIrwin authored
130 request.content_type = 'application/json'
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
131
d29b48a @ConradIrwin Always retry once [closes #6]
ConradIrwin authored
132 retried = false
0f36cc8 @ConradIrwin Add support for persistent authentication
ConradIrwin authored
133
d29b48a @ConradIrwin Always retry once [closes #6]
ConradIrwin authored
134 begin
5127b8f @kui Make code more testable
kui authored
135 response = http(api_url, request)
d29b48a @ConradIrwin Always retry once [closes #6]
ConradIrwin authored
136 if Net::HTTPSuccess === response
70548de @ConradIrwin Shorten URL before copying to clipboard
ConradIrwin authored
137 on_success(response.body, options)
d29b48a @ConradIrwin Always retry once [closes #6]
ConradIrwin authored
138 else
139 raise "Got #{response.class} from gist: #{response.body}"
140 end
141 rescue => e
142 raise if retried
143 retried = true
144 retry
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
145 end
d29b48a @ConradIrwin Always retry once [closes #6]
ConradIrwin authored
146
a6d40fa @ConradIrwin Make error handling a bit more friendly [Issue #4]
ConradIrwin authored
147 rescue => e
148 raise e.extend Error
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
149 end
150
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
151 # List all gists(private also) for authenticated user
152 # otherwise list public gists for given username (optional argument)
153 #
154 # @param [String] user
155 #
156 # see https://developer.github.com/v3/gists/#list-gists
72d1b13 @kapadia use link header for subsequent requests
kapadia authored
157 def list_gists(user = "")
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
158 url = "#{base_path}"
159
160 if user == ""
f4c3148 @pravj add method to handle access token internally
pravj authored
161 access_token = auth_token()
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
162 if access_token.to_s != ''
72d1b13 @kapadia use link header for subsequent requests
kapadia authored
163 url << "/gists?access_token=" << CGI.escape(access_token)
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
164
165 request = Net::HTTP::Get.new(url)
166 response = http(api_url, request)
167
168 pretty_gist(response)
169
170 else
90fb58f @ConradIrwin Tidy up output a little
ConradIrwin authored
171 raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
172 end
173
174 else
175 url << "/users/#{user}/gists"
176
51930f7 @ConradIrwin Minor fmt
ConradIrwin authored
177 request = Net::HTTP::Get.new(url)
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
178 response = http(api_url, request)
179
180 pretty_gist(response)
181 end
182 end
183
72d1b13 @kapadia use link header for subsequent requests
kapadia authored
184 def list_all_gists(user = "")
550d744 @kapadia request all pages when listing gists
kapadia authored
185 url = "#{base_path}"
186
187 if user == ""
188 access_token = auth_token()
189 if access_token.to_s != ''
190 url << "/gists?access_token=" << CGI.escape(access_token)
191 get_gist_pages(url)
192 else
193 raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
194 end
195
196 else
197 url << "/users/#{user}/gists"
198 get_gist_pages(url)
199 end
200
201 end
202
203 def get_gist_pages(url)
204
205 request = Net::HTTP::Get.new(url)
206 response = http(api_url, request)
72d1b13 @kapadia use link header for subsequent requests
kapadia authored
207 pretty_gist(response)
550d744 @kapadia request all pages when listing gists
kapadia authored
208
72d1b13 @kapadia use link header for subsequent requests
kapadia authored
209 link_header = response.header['link']
550d744 @kapadia request all pages when listing gists
kapadia authored
210
72d1b13 @kapadia use link header for subsequent requests
kapadia authored
211 if link_header
212 links = Hash[ link_header.gsub(/(<|>|")/, "").split(',').map { |link| link.split('; rel=') } ].invert
213 get_gist_pages(links['next']) if links['next']
550d744 @kapadia request all pages when listing gists
kapadia authored
214 end
215
216 end
217
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
218 # return prettified string result of response body for all gists
219 #
220 # @params [Net::HTTPResponse] response
221 # @return [String] prettified result of listing all gists
222 #
223 # see https://developer.github.com/v3/gists/#response
224 def pretty_gist(response)
225 body = JSON.parse(response.body)
226 if response.code == '200'
227 body.each do |gist|
90fb58f @ConradIrwin Tidy up output a little
ConradIrwin authored
228 puts "#{gist['html_url']} #{gist['description'] || gist['files'].keys.join(" ")} #{gist['public'] ? '' : '(secret)'}\n"
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
229 end
230
231 else
1f087b5 @pravj changes output format to be used programatically
pravj authored
232 raise Error, body['message']
a60af09 @pravj implements feature to list {all gists : authed user} & {public gists …
pravj authored
233 end
234 end
235
360d77b @ConradIrwin Typo
ConradIrwin authored
236 # Convert long github urls into short git.io ones
8251a77 @dlo add link shortening option
dlo authored
237 #
f25aa47 @ConradIrwin Still return full payload with :shorten => true
ConradIrwin authored
238 # @param [String] url
239 # @return [String] shortened url, or long url if shortening fails
8251a77 @dlo add link shortening option
dlo authored
240 def shorten(url)
8e64022 @mattn post URL to git.io via proxy.
mattn authored
241 request = Net::HTTP::Post.new("/")
242 request.set_form_data(:url => url)
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
243 response = http(GIT_IO_URL, request)
8251a77 @dlo add link shortening option
dlo authored
244 case response.code
245 when "201"
246 response['Location']
247 else
248 url
249 end
250 end
251
9033398 @Fleurer add Gist#rawify
Fleurer authored
252 # Convert github url into raw file url
253 #
67ce687 @Fleurer add comment for rawify
Fleurer authored
254 # Unfortunately the url returns from github's api is legacy,
255 # we have to taking a HTTPRedirection before appending it
256 # with '/raw'. Let's looking forward for github's api fix :)
257 #
9033398 @Fleurer add Gist#rawify
Fleurer authored
258 # @param [String] url
259 # @return [String] the raw file url
260 def rawify(url)
261 uri = URI(url)
262 request = Net::HTTP::Get.new(uri.path)
263 response = http(uri, request)
264 if Net::HTTPSuccess === response
265 url + '/raw'
266 elsif Net::HTTPRedirection === response
267 rawify(response.header['location'])
268 end
269 end
270
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
271 # Log the user into gist.
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
272 #
273 # This method asks the user for a username and password, and tries to obtain
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
274 # and OAuth2 access token, which is then stored in ~/.gist
a6d40fa @ConradIrwin Make error handling a bit more friendly [Issue #4]
ConradIrwin authored
275 #
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
276 # @raise [Gist::Error] if something went wrong
dd6f0c9 @ConradIrwin docs in comments
ConradIrwin authored
277 # @param [Hash] credentials login details
278 # @option credentials [String] :username
279 # @option credentials [String] :password
a6d40fa @ConradIrwin Make error handling a bit more friendly [Issue #4]
ConradIrwin authored
280 # @see http://developer.github.com/v3/oauth/
dd6f0c9 @ConradIrwin docs in comments
ConradIrwin authored
281 def login!(credentials={})
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
282 puts "Obtaining OAuth2 access_token from github."
7688687 @ConradIrwin Handle login failure nicely [Fixes #154]
ConradIrwin authored
283 loop do
284 print "GitHub username: "
285 username = credentials[:username] || $stdin.gets.strip
286 print "GitHub password: "
287 password = credentials[:password] || begin
288 `stty -echo` rescue nil
289 $stdin.gets.strip
290 ensure
291 `stty echo` rescue nil
292 end
552a054 @ConradIrwin Support 2-factor auth
ConradIrwin authored
293 puts ""
294
7688687 @ConradIrwin Handle login failure nicely [Fixes #154]
ConradIrwin authored
295 request = Net::HTTP::Post.new("#{base_path}/authorizations")
296 request.body = JSON.dump({
297 :scopes => [:gist],
a65f64e @ConradIrwin Ensure github tokens have a unique description
ConradIrwin authored
298 :note => "The gist gem (#{Time.now})",
7688687 @ConradIrwin Handle login failure nicely [Fixes #154]
ConradIrwin authored
299 :note_url => "https://github.com/ConradIrwin/gist"
300 })
301 request.content_type = 'application/json'
302 request.basic_auth(username, password)
303
552a054 @ConradIrwin Support 2-factor auth
ConradIrwin authored
304 response = http(api_url, request)
305
7688687 @ConradIrwin Handle login failure nicely [Fixes #154]
ConradIrwin authored
306 if Net::HTTPUnauthorized === response && response['X-GitHub-OTP']
307 print "2-factor auth code: "
308 twofa_code = $stdin.gets.strip
309 puts ""
310
311 request['X-GitHub-OTP'] = twofa_code
312 response = http(api_url, request)
313 end
314
315 if Net::HTTPCreated === response
59bf572 @jasonkarns AuthTokenFile needn't be a class (no state). just functions
jasonkarns authored
316 AuthTokenFile.write JSON.parse(response.body)['token']
7688687 @ConradIrwin Handle login failure nicely [Fixes #154]
ConradIrwin authored
317 puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/applications"
318 return
319 elsif Net::HTTPUnauthorized === response
320 puts "Error: #{JSON.parse(response.body)['message']}"
321 next
322 else
323 raise "Got #{response.class} from gist: #{response.body}"
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
324 end
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
325 end
a6d40fa @ConradIrwin Make error handling a bit more friendly [Issue #4]
ConradIrwin authored
326 rescue => e
327 raise e.extend Error
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
328 end
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
329
0e755de @mattn add comment.
mattn authored
330 # Return HTTP connection
331 #
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
332 # @param [URI::HTTP] The URI to which to connect
0e755de @mattn add comment.
mattn authored
333 # @return [Net::HTTP]
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
334 def http_connection(uri)
7b7ec0a @ConradIrwin Fix spec...
ConradIrwin authored
335 env = ENV['http_proxy'] || ENV['HTTP_PROXY']
b61e891 @ConradIrwin Picky style tweaks
ConradIrwin authored
336 connection = if env
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
337 proxy = URI(env)
338 Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port)
b61e891 @ConradIrwin Picky style tweaks
ConradIrwin authored
339 else
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
340 Net::HTTP.new(uri.host, uri.port)
b61e891 @ConradIrwin Picky style tweaks
ConradIrwin authored
341 end
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
342 if uri.scheme == "https"
8e64022 @mattn post URL to git.io via proxy.
mattn authored
343 connection.use_ssl = true
344 connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
345 end
48e6f40 @ConradIrwin up timeout a little so I can jist while tethered :)
ConradIrwin authored
346 connection.open_timeout = 10
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
347 connection.read_timeout = 10
41cf577 @mattn export http_connection to return connection, and added spec.
mattn authored
348 connection
349 end
350
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
351 # Run an HTTP operation
41cf577 @mattn export http_connection to return connection, and added spec.
mattn authored
352 #
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
353 # @param [URI::HTTP] The URI to which to connect
354 # @param [Net::HTTPRequest] The request to make
41cf577 @mattn export http_connection to return connection, and added spec.
mattn authored
355 # @return [Net::HTTPResponse]
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
356 def http(url, request)
6a0943d @ConradIrwin Include a User-Agent [Fixes #34]
ConradIrwin authored
357 request['User-Agent'] = USER_AGENT
358
6ed6d70 @ConradIrwin Use URI objects, not hashes
ConradIrwin authored
359 http_connection(url).start do |http|
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
360 http.request request
361 end
a6d40fa @ConradIrwin Make error handling a bit more friendly [Issue #4]
ConradIrwin authored
362 rescue Timeout::Error
fa383c1 @kui Use GITHUB_URL instead of GHE_URL
kui authored
363 raise "Could not connect to #{api_url}"
1d34fb1 @ConradIrwin Use Oauth instead of username/password
ConradIrwin authored
364 end
37f00cb @rking Add clipboard support and tests
rking authored
365
366 # Called after an HTTP response to gist to perform post-processing.
367 #
4a98996 @ConradIrwin Try to clean up output stuff
ConradIrwin authored
368 # @param [String] body the text body from the github api
369 # @param [Hash] options more detailed options, see
370 # the documentation for {multi_gist}
37f00cb @rking Add clipboard support and tests
rking authored
371 def on_success(body, options={})
372 json = JSON.parse(body)
373
4a98996 @ConradIrwin Try to clean up output stuff
ConradIrwin authored
374 output = case options[:output]
375 when :javascript
376 %Q{<script src="#{json['html_url']}.js"></script>}
377 when :html_url
378 json['html_url']
9033398 @Fleurer add Gist#rawify
Fleurer authored
379 when :raw_url
380 rawify(json['html_url'])
4a98996 @ConradIrwin Try to clean up output stuff
ConradIrwin authored
381 when :short_url
382 shorten(json['html_url'])
66a2222 @Fleurer add -r option, and allow use combination with -s
Fleurer authored
383 when :short_raw_url
384 shorten(rawify(json['html_url']))
4a98996 @ConradIrwin Try to clean up output stuff
ConradIrwin authored
385 else
386 json
387 end
388
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
389 Gist.copy(output.to_s) if options[:copy]
390 Gist.open(json['html_url']) if options[:open]
37f00cb @rking Add clipboard support and tests
rking authored
391
4a98996 @ConradIrwin Try to clean up output stuff
ConradIrwin authored
392 output
37f00cb @rking Add clipboard support and tests
rking authored
393 end
394
395 # Copy a string to the clipboard.
396 #
397 # @param [String] content
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
398 # @raise [Gist::Error] if no clipboard integration could be found
37f00cb @rking Add clipboard support and tests
rking authored
399 #
400 def copy(content)
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
401 IO.popen(clipboard_command(:copy), 'r+') { |clip| clip.print content }
fb05748 @ConradIrwin Better error for mac+tmux [Fixes #23]
ConradIrwin authored
402
403 unless paste == content
404 message = 'Copying to clipboard failed.'
405
406 if ENV["TMUX"] && clipboard_command(:copy) == 'pbcopy'
407 message << "\nIf you're running tmux on a mac, try http://robots.thoughtbot.com/post/19398560514/how-to-copy-and-paste-with-tmux-on-mac-os-x"
408 end
409
410 raise Error, message
411 end
776427f @rking Spiffier Errors (internally + externally)
rking authored
412 rescue Error => e
413 raise ClipboardError, e.message + "\nAttempted to copy: #{content}"
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
414 end
37f00cb @rking Add clipboard support and tests
rking authored
415
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
416 # Get a string from the clipboard.
417 #
418 # @param [String] content
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
419 # @raise [Gist::Error] if no clipboard integration could be found
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
420 def paste
421 `#{clipboard_command(:paste)}`
422 end
423
c1a3ea0 @mattn gracefull error on windows.
mattn authored
424 # Find command from PATH environment.
425 #
426 # @param [String] cmd command name to find
427 # @param [String] options PATH environment variable
428 # @return [String] the command found
429 def which(cmd, path=ENV['PATH'])
430 if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin|cygwin/
431 path.split(File::PATH_SEPARATOR).each {|dir|
432 f = File.join(dir, cmd+".exe")
433 return f if File.executable?(f) && !File.directory?(f)
434 }
435 nil
436 else
437 return system("which #{cmd} > /dev/null 2>&1")
438 end
439 end
440
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
441 # Get the command to use for the clipboard action.
442 #
443 # @param [Symbol] action either :copy or :paste
444 # @return [String] the command to run
ca024d2 @ConradIrwin Mindless rename jist->gist
ConradIrwin authored
445 # @raise [Gist::ClipboardError] if no clipboard integration could be found
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
446 def clipboard_command(action)
447 command = CLIPBOARD_COMMANDS.keys.detect do |cmd|
c1a3ea0 @mattn gracefull error on windows.
mattn authored
448 which cmd
37f00cb @rking Add clipboard support and tests
rking authored
449 end
776427f @rking Spiffier Errors (internally + externally)
rking authored
450 raise ClipboardError, <<-EOT unless command
451 Could not find copy command, tried:
452 #{CLIPBOARD_COMMANDS.values.join(' || ')}
453 EOT
018d574 @ConradIrwin Add support for pasting (version bump)
ConradIrwin authored
454 action == :copy ? command : CLIPBOARD_COMMANDS[command]
37f00cb @rking Add clipboard support and tests
rking authored
455 end
472ba40 @ConradIrwin Add --open/-o as well
ConradIrwin authored
456
457 # Open a URL in a browser.
458 #
459 # @param [String] url
460 # @raise [RuntimeError] if no browser integration could be found
461 #
462 # This method was heavily inspired by defunkt's Gist#open,
463 # @see https://github.com/defunkt/gist/blob/bca9b29/lib/gist.rb#L157
464 def open(url)
465 command = if ENV['BROWSER']
466 ENV['BROWSER']
467 elsif RUBY_PLATFORM =~ /darwin/
468 'open'
469 elsif RUBY_PLATFORM =~ /linux/
e07a231 @rking Try harder to find a browser on Linux
rking authored
470 %w(
471 sensible-browser
360e3e7 @EvanPurkhiser [Linux] Support the xdg-open command
EvanPurkhiser authored
472 xdg-open
e07a231 @rking Try harder to find a browser on Linux
rking authored
473 firefox
474 firefox-bin
475 ).detect do |cmd|
476 which cmd
477 end
472ba40 @ConradIrwin Add --open/-o as well
ConradIrwin authored
478 elsif ENV['OS'] == 'Windows_NT' || RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw|wince/i
479 'start ""'
480 else
481 raise "Could not work out how to use a browser."
482 end
483
484 `#{command} #{url}`
485 end
5127b8f @kui Make code more testable
kui authored
486
487 # Get the API base path
488 def base_path
fa383c1 @kui Use GITHUB_URL instead of GHE_URL
kui authored
489 ENV.key?(URL_ENV_NAME) ? GHE_BASE_PATH : GITHUB_BASE_PATH
5127b8f @kui Make code more testable
kui authored
490 end
491
492 # Get the API URL
493 def api_url
fa383c1 @kui Use GITHUB_URL instead of GHE_URL
kui authored
494 ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_API_URL
5127b8f @kui Make code more testable
kui authored
495 end
f8db4b1 @ConradIrwin Store enterprise credentials in a separate file
ConradIrwin authored
496
7be4a12 @ConradIrwin Support for `git config --global gist.private yes`
ConradIrwin authored
497 def legacy_private_gister?
498 return unless which('git')
499 `git config --global gist.private` =~ /\Ayes|1|true|on\z/i
500 end
501
502 def should_be_public?(options={})
3831471 @ConradIrwin Support --no-private
ConradIrwin authored
503 if options.key? :private
504 !options[:private]
505 else
506 !Gist.legacy_private_gister?
507 end
7be4a12 @ConradIrwin Support for `git config --global gist.private yes`
ConradIrwin authored
508 end
fb82c87 @ConradIrwin The simplest gist gem evar!
ConradIrwin authored
509 end
Something went wrong with that request. Please try again.