gista 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/.yardopts +8 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +32 -0
- data/HISTORY.md +5 -0
- data/LICENSE +19 -0
- data/README.md +91 -0
- data/Rakefile +17 -0
- data/bin/gista +12 -0
- data/gista.gemspec +40 -0
- data/lib/gista.rb +53 -0
- data/lib/gista/api_request.rb +62 -0
- data/lib/gista/gist_request.rb +43 -0
- data/lib/gista/login_prompt.rb +55 -0
- data/lib/gista/options.rb +76 -0
- data/lib/gista/token_request.rb +39 -0
- data/lib/gista/user_token.rb +56 -0
- data/lib/gista/version.rb +3 -0
- data/spec/gista/gist_request_spec.rb +7 -0
- data/spec/gista/login_prompt_spec.rb +26 -0
- data/spec/gista/options_spec.rb +57 -0
- data/spec/gista/token_request_spec.rb +8 -0
- data/spec/gista/user_token_spec.rb +30 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/api_request_examples.rb +38 -0
- metadata +172 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gista (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.3)
|
10
|
+
fakeweb (1.3.0)
|
11
|
+
kramdown (0.14.0)
|
12
|
+
rake (0.9.2.2)
|
13
|
+
rspec (2.11.0)
|
14
|
+
rspec-core (~> 2.11.0)
|
15
|
+
rspec-expectations (~> 2.11.0)
|
16
|
+
rspec-mocks (~> 2.11.0)
|
17
|
+
rspec-core (2.11.1)
|
18
|
+
rspec-expectations (2.11.3)
|
19
|
+
diff-lcs (~> 1.1.3)
|
20
|
+
rspec-mocks (2.11.3)
|
21
|
+
yard (0.8.2.1)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
fakeweb (~> 1.3)
|
28
|
+
gista!
|
29
|
+
kramdown
|
30
|
+
rake (~> 0.9)
|
31
|
+
rspec (~> 2.11)
|
32
|
+
yard (~> 0.8)
|
data/HISTORY.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (C) 2012 Arjan van der Gaag
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Gista -- create gists from the command line [![Build Status](https://secure.travis-ci.org/avdgaag/gista.png?branch=master)](http://travis-ci.org/avdgaag/gista) [![Gemnasium dependency status](https://gemnasium.com/avdgaag/gista.png)](https://gemnasium.com/avdgaag/gista)
|
2
|
+
|
3
|
+
## Introduction
|
4
|
+
|
5
|
+
Gista is a very simple command-line program and Ruby library for creating
|
6
|
+
Gists. As a stand-alone program, it can create a new gist from files listed as
|
7
|
+
arguments, or by reading from STDIN.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Gista is distributed as a Ruby gem, which should be installed on most Macs and
|
12
|
+
Linux systems. Once you have ensured you have a working installation of Ruby
|
13
|
+
and Ruby gems, install the gem as follows from the command line:
|
14
|
+
|
15
|
+
$ gem install gista
|
16
|
+
|
17
|
+
You can verify the gem has installed correctly by checking its version number:
|
18
|
+
|
19
|
+
$ gista -v
|
20
|
+
|
21
|
+
If this generates an error, something has gone wrong. You should see something
|
22
|
+
along the lines of `gista 1.0.0`.
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
You can list one or more filenames as an argument:
|
27
|
+
|
28
|
+
$ gista filename1 filename2...
|
29
|
+
|
30
|
+
Or, you can read content from STDIN:
|
31
|
+
|
32
|
+
$ echo "Hello, world!" | gista
|
33
|
+
|
34
|
+
When reading from STDIN one file will be created, by default called `untitled`.
|
35
|
+
You can override this name:
|
36
|
+
|
37
|
+
$ echo "Hello, world!" | gista -f "my new gist"
|
38
|
+
|
39
|
+
By default, new gists are private. You can make a public gist with the `-p`
|
40
|
+
option:
|
41
|
+
|
42
|
+
$ gista -p filename1
|
43
|
+
$ gista --public filename1
|
44
|
+
|
45
|
+
When everything went according to plan, the program will output the URL to
|
46
|
+
the newly created gist.
|
47
|
+
|
48
|
+
If you want to use Gista as a Ruby library, take a look at the `bin/gista`
|
49
|
+
file for an example how to use it:
|
50
|
+
|
51
|
+
authoriser = Gista::TokenRequest.new(Gista::LoginPrompt.new)
|
52
|
+
token = Gista::UserToken.new(authoriser).token
|
53
|
+
options = Gista::Options.new(ARGV).options
|
54
|
+
puts Gista.post_and_get_url(token, options)
|
55
|
+
|
56
|
+
### Documentation
|
57
|
+
|
58
|
+
See the inline [API
|
59
|
+
docs](http://rubydoc.info/github/avdgaag/gista/master/frames) for more
|
60
|
+
information.
|
61
|
+
|
62
|
+
## Other
|
63
|
+
|
64
|
+
### Note on Patches/Pull Requests
|
65
|
+
|
66
|
+
1. Fork the project.
|
67
|
+
2. Make your feature addition or bug fix.
|
68
|
+
3. Add tests for it. This is important so I don't break it in a future version
|
69
|
+
unintentionally.
|
70
|
+
4. Commit, do not mess with rakefile, version, or history. (if you want to have
|
71
|
+
your own version, that is fine but bump version in a commit by itself I can
|
72
|
+
ignore when I pull)
|
73
|
+
5. Send me a pull request. Bonus points for topic branches.
|
74
|
+
|
75
|
+
### Issues
|
76
|
+
|
77
|
+
Please report any issues, defects or suggestions in the [Github issue
|
78
|
+
tracker](https://github.com/avdgaag/gista/issues).
|
79
|
+
|
80
|
+
### What has changed?
|
81
|
+
|
82
|
+
See the [HISTORY](https://github.com/avdgaag/gista/blob/master/HISTORY.md) file
|
83
|
+
for a detailed changelog.
|
84
|
+
|
85
|
+
### Credits
|
86
|
+
|
87
|
+
Created by: Arjan van der Gaag
|
88
|
+
URL: [http://arjanvandergaag.nl](http://arjanvandergaag.nl)
|
89
|
+
Project homepage: [http://avdgaag.github.com/gista](http://avdgaag.github.com/gista)
|
90
|
+
Date: april 2012
|
91
|
+
License: [MIT-license](https://github.com/avdgaag/gista/LICENSE) (same as Ruby)
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
Bundler.setup
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
require 'yard'
|
9
|
+
|
10
|
+
desc 'Default: run specs.'
|
11
|
+
task default: :spec
|
12
|
+
|
13
|
+
desc 'Run specs'
|
14
|
+
RSpec::Core::RakeTask.new
|
15
|
+
|
16
|
+
desc 'Generate API docs'
|
17
|
+
YARD::Rake::YardocTask.new
|
data/bin/gista
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'gista'
|
4
|
+
|
5
|
+
begin
|
6
|
+
authoriser = Gista::TokenRequest.new(Gista::LoginPrompt.new)
|
7
|
+
token = Gista::UserToken.new(authoriser).token
|
8
|
+
options = Gista::Options.new(ARGV).options
|
9
|
+
puts Gista.post_and_get_url(token, options)
|
10
|
+
rescue StandardError => e
|
11
|
+
abort e.message
|
12
|
+
end
|
data/gista.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/gista/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
# Metadata
|
6
|
+
s.name = 'gista'
|
7
|
+
s.version = Gista::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Arjan van der Gaag']
|
10
|
+
s.email = %q{arjan@arjanvandergaag.nl}
|
11
|
+
s.description = %q{create gists from the command line}
|
12
|
+
s.homepage = %q{http://avdgaag.github.com/gista}
|
13
|
+
s.summary = <<-EOS
|
14
|
+
Gista is a very simple command-line program and Ruby library for creating
|
15
|
+
Gists. As a stand-alone program, it can create a new gist from files listed as
|
16
|
+
arguments, or by reading from STDIN.
|
17
|
+
EOS
|
18
|
+
|
19
|
+
# Files
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ['lib']
|
24
|
+
|
25
|
+
# Rdoc
|
26
|
+
s.rdoc_options = ['--charset=UTF-8']
|
27
|
+
s.extra_rdoc_files = [
|
28
|
+
'LICENSE',
|
29
|
+
'README.md',
|
30
|
+
'HISTORY.md'
|
31
|
+
]
|
32
|
+
|
33
|
+
# Dependencies
|
34
|
+
s.add_development_dependency 'yard', '~> 0.8'
|
35
|
+
s.add_development_dependency 'fakeweb', '~> 1.3'
|
36
|
+
s.add_development_dependency 'rspec', '~> 2.11'
|
37
|
+
s.add_development_dependency 'rake', '~> 0.9'
|
38
|
+
s.add_development_dependency 'kramdown'
|
39
|
+
end
|
40
|
+
|
data/lib/gista.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'gista/version'
|
2
|
+
require 'gista/api_request'
|
3
|
+
require 'gista/token_request'
|
4
|
+
require 'gista/gist_request'
|
5
|
+
require 'gista/login_prompt'
|
6
|
+
require 'gista/user_token'
|
7
|
+
require 'gista/options'
|
8
|
+
|
9
|
+
# Gista allows for easily creating Gists from the command line or a Ruby
|
10
|
+
# program. For full instructions on how to use it from the command line, run
|
11
|
+
# `gista --help`.
|
12
|
+
#
|
13
|
+
# @example From the command line, listing filenames
|
14
|
+
# $ gista my_file.rb another_file.rb
|
15
|
+
# https://gist.github.com/...
|
16
|
+
#
|
17
|
+
# @example From the command line, reading STDIN
|
18
|
+
# $ rspec | gista
|
19
|
+
# https://gist.github.com/...
|
20
|
+
# $ gista < my_file.rb
|
21
|
+
# https://gist.github.com/...
|
22
|
+
#
|
23
|
+
# @example From a Ruby program
|
24
|
+
# require 'gista'
|
25
|
+
# Gista.post_and_get_url(oauth_token, options)
|
26
|
+
#
|
27
|
+
module Gista
|
28
|
+
# Exception raised when trying to do stuff on Github that you are not
|
29
|
+
# authorized for.
|
30
|
+
class Unauthorized < StandardError
|
31
|
+
def initialize(*args)
|
32
|
+
super "Authorization with Github failed. Please remove your token file and try again to re-authenticate.", *args
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Exception raised when the request does not respond as we expected.
|
37
|
+
class RequestError < StandardError
|
38
|
+
def initialize(*args)
|
39
|
+
super *args.unshift("Github responded with #{args.shift}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module_function
|
44
|
+
|
45
|
+
# Make a request to the Github API using the given authorization token
|
46
|
+
# and options. Returns the URL to the Gist if it was successful.
|
47
|
+
#
|
48
|
+
# @param token <String>
|
49
|
+
# @param options <Hash>
|
50
|
+
def post_and_get_url(token, options)
|
51
|
+
GistRequest.new(token, options).fetch('html_url')
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Gista
|
6
|
+
# Base class for JSON requests to the Github API. ApiRequest knows how to send
|
7
|
+
# a request with a JSON body and parse the results. Use `fetch` to read values
|
8
|
+
# from the JSON response.
|
9
|
+
#
|
10
|
+
# Raises an exception when not authorized or when the API request responds
|
11
|
+
# with something other than an OK.
|
12
|
+
class ApiRequest
|
13
|
+
URL = 'api.github.com'
|
14
|
+
PORT = 443
|
15
|
+
OPEN_TIMEOUT = 10
|
16
|
+
READ_TIMEOUT = 10
|
17
|
+
CONTENT_TYPE = 'application/json'
|
18
|
+
|
19
|
+
# Read a key from the response.
|
20
|
+
def fetch(key)
|
21
|
+
result.fetch(key)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def make_post(options = {})
|
27
|
+
Net::HTTP::Post.new(path).tap do |request|
|
28
|
+
request.content_type = CONTENT_TYPE
|
29
|
+
request.body = JSON.dump(options)
|
30
|
+
yield request if block_given?
|
31
|
+
request
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def connection
|
36
|
+
@connection ||= Net::HTTP.new(URL, PORT).tap do |connection|
|
37
|
+
connection.use_ssl = true
|
38
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
39
|
+
connection.open_timeout = OPEN_TIMEOUT
|
40
|
+
connection.read_timeout = READ_TIMEOUT
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def result
|
45
|
+
@result ||= JSON.parse(response.body)
|
46
|
+
end
|
47
|
+
|
48
|
+
def response
|
49
|
+
@response ||= begin
|
50
|
+
connection.start { |http| http.request(request) }.tap do |r|
|
51
|
+
if Net::HTTPUnauthorized === r
|
52
|
+
raise Unauthorized
|
53
|
+
elsif not Net::HTTPCreated === r
|
54
|
+
raise RequestError, r.class
|
55
|
+
end
|
56
|
+
end
|
57
|
+
rescue Timeout::Error
|
58
|
+
raise 'Could not connect to Github API'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Gista
|
2
|
+
# Special Github API request to create a new gist. It requires an OAuth token
|
3
|
+
# for authorization, and options for what file to post.
|
4
|
+
#
|
5
|
+
# See the API docs for what options are expected to create a new Gist.
|
6
|
+
#
|
7
|
+
# @example Creating a Gist
|
8
|
+
# GistRequest.new('acc829df', {
|
9
|
+
# public: false,
|
10
|
+
# files: {
|
11
|
+
# file1: { content: 'my content' }
|
12
|
+
# }
|
13
|
+
# }).fetch('html_url')
|
14
|
+
# # => "http://gist.github.com/fbed697547047a733434"
|
15
|
+
#
|
16
|
+
# @see http://developer.github.com/v3/gists/#create-a-gist
|
17
|
+
class GistRequest < ApiRequest
|
18
|
+
# Authorization token to authorize with Github.
|
19
|
+
#
|
20
|
+
# @see http://developer.github.com/v3/oauth/#scopes
|
21
|
+
# @see Gista::TokenRequest
|
22
|
+
# @return [String]
|
23
|
+
attr_reader :token
|
24
|
+
|
25
|
+
# Options containing information about files, filenames and visibility
|
26
|
+
# @return [Hash]
|
27
|
+
attr_reader :options
|
28
|
+
|
29
|
+
def initialize(token, options = {})
|
30
|
+
@token, @options = token, options
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def request
|
36
|
+
make_post(options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def path
|
40
|
+
"/gists?access_token=#{CGI.escape(token)}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Gista
|
2
|
+
# Create an object that responds to `username` and `password` messages by
|
3
|
+
# prompting the user on the command line. Helpful when creating a
|
4
|
+
# command-line client. When using as a library, simply substitute with a
|
5
|
+
# `Struct` or something similar.
|
6
|
+
#
|
7
|
+
# @example Asking the user for username and password
|
8
|
+
# lp = LoginPrompt.new
|
9
|
+
# lp.username
|
10
|
+
# # ... prompt for username and password...
|
11
|
+
# # => "username"
|
12
|
+
# lp.password
|
13
|
+
# # => "password"
|
14
|
+
#
|
15
|
+
class LoginPrompt
|
16
|
+
def initialize
|
17
|
+
@username = nil
|
18
|
+
@password = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return the username if set, or prompt the user to enter both his username
|
22
|
+
# and password.
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
def username
|
26
|
+
prompt unless @username
|
27
|
+
@username
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the password if set, or prompt the user to enter both his username
|
31
|
+
# and password.
|
32
|
+
#
|
33
|
+
# @return [String]
|
34
|
+
def password
|
35
|
+
prompt unless @password
|
36
|
+
@password
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def prompt
|
42
|
+
$stdout.puts 'Please authenticate with your Github credentials:'
|
43
|
+
$stdout.print 'Username: '
|
44
|
+
@username = $stdin.gets.strip
|
45
|
+
$stdout.print 'Password: '
|
46
|
+
@password = begin
|
47
|
+
`stty -echo` rescue nil
|
48
|
+
$stdin.gets.strip
|
49
|
+
ensure
|
50
|
+
`stty echo` rescue nil
|
51
|
+
end
|
52
|
+
$stdout.puts ''
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Gista
|
4
|
+
# Parse options given on the command line into an options hash suitable for a
|
5
|
+
# `GistRequest` object.
|
6
|
+
#
|
7
|
+
# This provides the default options:
|
8
|
+
#
|
9
|
+
# * Gists are private by default
|
10
|
+
# * When reading from `STDIN`, the file is titled 'untitled'
|
11
|
+
#
|
12
|
+
# When any options are left after parsing all the flags, they are treated as
|
13
|
+
# filenames. They will be added to the options as files in the Gist. When no
|
14
|
+
# arguments are left, input is read from `STDIN` and its contents will be
|
15
|
+
# added to the options hash as a single file.
|
16
|
+
#
|
17
|
+
# This also provides the `--help` and `--version` options for the
|
18
|
+
# command-line client.
|
19
|
+
class Options
|
20
|
+
# Command line options, flags and filenames
|
21
|
+
#
|
22
|
+
# @return [Array]
|
23
|
+
attr_reader :args
|
24
|
+
|
25
|
+
def initialize(args)
|
26
|
+
@args = args
|
27
|
+
@options = { public: false }
|
28
|
+
@stdin_filename = 'untitled'
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Hash]
|
32
|
+
def options
|
33
|
+
parse_options
|
34
|
+
parse_files
|
35
|
+
@options
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def parse_files
|
41
|
+
if args.any?
|
42
|
+
@options[:files] = args.inject({}) do |acc, filename|
|
43
|
+
acc.merge File.basename(filename) => { content: File.read(filename) }
|
44
|
+
end
|
45
|
+
else
|
46
|
+
@options[:files] = {
|
47
|
+
@stdin_filename => { content: $stdin.read }
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_options
|
53
|
+
OptionParser.new do |opts|
|
54
|
+
opts.banner = 'Usage: gista [options] [file..]'
|
55
|
+
opts.separator ''
|
56
|
+
opts.separator 'Gist options:'
|
57
|
+
opts.on('-p', '--[no-]public', 'Create a public gist') do |v|
|
58
|
+
@options[:public] = v
|
59
|
+
end
|
60
|
+
opts.on('-f', '--filename [FILENAME]', 'Specify filename to use when reading from STDIN') do |f|
|
61
|
+
@stdin_filename = f
|
62
|
+
end
|
63
|
+
opts.separator ''
|
64
|
+
opts.separator 'Generic options:'
|
65
|
+
opts.on_tail('-h', '--help', 'Show brief usage information') do
|
66
|
+
puts opts
|
67
|
+
exit
|
68
|
+
end
|
69
|
+
opts.on_tail('-v', '--version', 'Show version information') do
|
70
|
+
puts VERSION
|
71
|
+
exit
|
72
|
+
end
|
73
|
+
end.parse!(args)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Gista
|
2
|
+
# Request to the Github API to authorize with a username and password and
|
3
|
+
# acquire an OAuth token, which can be stored and re-used for password-less
|
4
|
+
# authorization.
|
5
|
+
#
|
6
|
+
# @example Request a token
|
7
|
+
# credentials = OpenStruct.new(username: 'foo', password: 'bar')
|
8
|
+
# TokenRequest.new(credentials).fetch('token')
|
9
|
+
#
|
10
|
+
# @see http://developer.github.com/v3/oauth/#scopes
|
11
|
+
class TokenRequest < ApiRequest
|
12
|
+
# An object that contains a username and password
|
13
|
+
#
|
14
|
+
# @see LoginPrompt
|
15
|
+
# @return [#username, #password]
|
16
|
+
attr_reader :credentials
|
17
|
+
|
18
|
+
def initialize(credentials)
|
19
|
+
@credentials = credentials
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def path
|
25
|
+
'/authorizations'
|
26
|
+
end
|
27
|
+
|
28
|
+
def request
|
29
|
+
options = {
|
30
|
+
scopes: [:gist],
|
31
|
+
note: 'Gista gem',
|
32
|
+
note_url: 'http://avdgaag.github.com'
|
33
|
+
}
|
34
|
+
make_post(options) do |request|
|
35
|
+
request.basic_auth(credentials.username, credentials.password)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Gista
|
2
|
+
# Acquire and store an OAuth authorization token in the user's home directory.
|
3
|
+
#
|
4
|
+
# @see TokenRequest
|
5
|
+
class UserToken
|
6
|
+
|
7
|
+
# Default path to file to store the token in
|
8
|
+
#
|
9
|
+
# @return [String]
|
10
|
+
DEFAULT_CONFIG_FILE = File.expand_path('~/.gista')
|
11
|
+
|
12
|
+
# @return [#fetch] any object that returns the token to use
|
13
|
+
# when calling `fetch('token')`.
|
14
|
+
attr_reader :authoriser
|
15
|
+
|
16
|
+
def initialize(authoriser, token_file = nil)
|
17
|
+
@authoriser, @token_file = authoriser, token_file
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return the user token. Either read it from disk, or request a new token
|
21
|
+
# with the Github API using the credentials from `creds`.
|
22
|
+
#
|
23
|
+
# @return [String]
|
24
|
+
def token
|
25
|
+
return read_from_config_file if has_config_file?
|
26
|
+
request_token.tap do |token|
|
27
|
+
write_to_config_file(token)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [String] path to file containing a re-usable token
|
32
|
+
def token_file
|
33
|
+
@token_file || DEFAULT_CONFIG_FILE
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def request_token
|
39
|
+
authoriser.fetch('token')
|
40
|
+
end
|
41
|
+
|
42
|
+
def write_to_config_file(content)
|
43
|
+
File.open(token_file, 'w') do |f|
|
44
|
+
f.write content
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def read_from_config_file
|
49
|
+
File.read(token_file).chomp
|
50
|
+
end
|
51
|
+
|
52
|
+
def has_config_file?
|
53
|
+
File.exist?(token_file)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gista::LoginPrompt do
|
4
|
+
let(:output) { StringIO.new }
|
5
|
+
let(:input) { StringIO.new("username\npassword\n") }
|
6
|
+
|
7
|
+
before do
|
8
|
+
$stdout = output
|
9
|
+
$stdin = input
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
$stdout = STDOUT
|
14
|
+
$stdin = STDIN
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should ask for username and password' do
|
18
|
+
subject.username
|
19
|
+
output.string.should include('Please authenticate with your Github credentials:')
|
20
|
+
output.string.should include('Username: ')
|
21
|
+
output.string.should include('Password: ')
|
22
|
+
end
|
23
|
+
|
24
|
+
its(:username) { should == 'username' }
|
25
|
+
its(:password) { should == 'password' }
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gista::Options do
|
4
|
+
let(:input) { StringIO.new('input') }
|
5
|
+
let(:subject) { described_class.new(args).options }
|
6
|
+
|
7
|
+
before { $stdin = input }
|
8
|
+
|
9
|
+
context 'without options' do
|
10
|
+
let(:args) { [] }
|
11
|
+
its([:public]) { should be_false }
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'with public switch' do
|
15
|
+
let(:args) { ['-p'] }
|
16
|
+
its([:public]) { should be_true }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with filenames that exist' do
|
20
|
+
let(:args) { ['/tmp/gista_file1', '/tmp/gista_file2'] }
|
21
|
+
|
22
|
+
before do
|
23
|
+
args.each_with_index do |filename, i|
|
24
|
+
File.open(filename, 'w') { |f| f.write "gista example #{i}" }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
after do
|
29
|
+
args.each { |filename| File.unlink(filename) if File.exists?(filename) }
|
30
|
+
end
|
31
|
+
|
32
|
+
its([:files]) do
|
33
|
+
should == {
|
34
|
+
'gista_file1' => { content: 'gista example 0' },
|
35
|
+
'gista_file2' => { content: 'gista example 1' }
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with filenames that do not exist' do
|
41
|
+
let(:args) { ['foo'] }
|
42
|
+
|
43
|
+
it 'should raise exception' do
|
44
|
+
expect { subject }.to raise_error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'reading content from STDIN with default filename' do
|
49
|
+
let(:args) { ['-f', 'bla'] }
|
50
|
+
its([:files]) { should == { 'bla' => { content: 'input' } } }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'reading content from STDIN with default filename' do
|
54
|
+
let(:args) { [] }
|
55
|
+
its([:files]) { should == { 'untitled' => { content: 'input' } } }
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gista::TokenRequest do
|
4
|
+
let(:credentials) { double('credentials', username: 'username', password: 'password') }
|
5
|
+
let(:url) { 'https://username:password@api.github.com/authorizations' }
|
6
|
+
let(:subject) { described_class.new(credentials) }
|
7
|
+
it_should_behave_like 'API request'
|
8
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gista::UserToken do
|
4
|
+
let(:token_file) { '/tmp/gista_token' }
|
5
|
+
let(:authoriser) { double('auth', fetch: 'token') }
|
6
|
+
let(:subject) { described_class.new authoriser, token_file }
|
7
|
+
|
8
|
+
after do
|
9
|
+
File.unlink(token_file) if File.exist?(token_file)
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when the token file exists' do
|
13
|
+
before do
|
14
|
+
File.open(token_file, 'w') { |f| f.write 'foo' }
|
15
|
+
end
|
16
|
+
|
17
|
+
its(:token) { should == 'foo' }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when the token file does not exist' do
|
21
|
+
it 'writes token to file' do
|
22
|
+
File.exist?(token_file).should be_false
|
23
|
+
subject.token
|
24
|
+
File.exist?(token_file).should be_true
|
25
|
+
File.read(token_file).should == 'token'
|
26
|
+
end
|
27
|
+
|
28
|
+
its(:token) { should == 'token' }
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require :development
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
5
|
+
require 'gista'
|
6
|
+
require File.expand_path('../support/api_request_examples', __FILE__)
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.before do
|
10
|
+
FakeWeb.allow_net_connect = false
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
shared_examples_for 'API request' do
|
2
|
+
before do
|
3
|
+
FakeWeb.register_uri(:post, url, options)
|
4
|
+
end
|
5
|
+
|
6
|
+
context 'when successful' do
|
7
|
+
let(:options) do
|
8
|
+
{ body: '{"foo":"bar"}',
|
9
|
+
content_type: 'application/json',
|
10
|
+
status: [201, 'Created'] }
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should request json at Github' do
|
14
|
+
subject.fetch('foo')
|
15
|
+
FakeWeb.last_request.content_type.should == 'application/json'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should parse the body as JSON' do
|
19
|
+
subject.fetch('foo').should == 'bar'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when not OK' do
|
24
|
+
let(:options) { { body: '', status: [301, 'Redirect'] } }
|
25
|
+
|
26
|
+
it 'should raise' do
|
27
|
+
expect { subject.fetch('foo') }.to raise_error(Gista::RequestError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when not authorized' do
|
32
|
+
let(:options) { { body: '', status: [401, 'Not authorized'] } }
|
33
|
+
|
34
|
+
it 'should raise' do
|
35
|
+
expect { subject.fetch('foo') }.to raise_error(Gista::Unauthorized)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gista
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Arjan van der Gaag
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: yard
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.8'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.8'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: fakeweb
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.3'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.3'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.11'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.11'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.9'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0.9'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: kramdown
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: create gists from the command line
|
95
|
+
email: arjan@arjanvandergaag.nl
|
96
|
+
executables:
|
97
|
+
- gista
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files:
|
100
|
+
- LICENSE
|
101
|
+
- README.md
|
102
|
+
- HISTORY.md
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- .rspec
|
106
|
+
- .travis.yml
|
107
|
+
- .yardopts
|
108
|
+
- Gemfile
|
109
|
+
- Gemfile.lock
|
110
|
+
- HISTORY.md
|
111
|
+
- LICENSE
|
112
|
+
- README.md
|
113
|
+
- Rakefile
|
114
|
+
- bin/gista
|
115
|
+
- gista.gemspec
|
116
|
+
- lib/gista.rb
|
117
|
+
- lib/gista/api_request.rb
|
118
|
+
- lib/gista/gist_request.rb
|
119
|
+
- lib/gista/login_prompt.rb
|
120
|
+
- lib/gista/options.rb
|
121
|
+
- lib/gista/token_request.rb
|
122
|
+
- lib/gista/user_token.rb
|
123
|
+
- lib/gista/version.rb
|
124
|
+
- spec/gista/gist_request_spec.rb
|
125
|
+
- spec/gista/login_prompt_spec.rb
|
126
|
+
- spec/gista/options_spec.rb
|
127
|
+
- spec/gista/token_request_spec.rb
|
128
|
+
- spec/gista/user_token_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
- spec/support/api_request_examples.rb
|
131
|
+
homepage: http://avdgaag.github.com/gista
|
132
|
+
licenses: []
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options:
|
135
|
+
- --charset=UTF-8
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
segments:
|
145
|
+
- 0
|
146
|
+
hash: -3780918806351321672
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
segments:
|
154
|
+
- 0
|
155
|
+
hash: -3780918806351321672
|
156
|
+
requirements: []
|
157
|
+
rubyforge_project:
|
158
|
+
rubygems_version: 1.8.24
|
159
|
+
signing_key:
|
160
|
+
specification_version: 3
|
161
|
+
summary: Gista is a very simple command-line program and Ruby library for creating
|
162
|
+
Gists. As a stand-alone program, it can create a new gist from files listed as arguments,
|
163
|
+
or by reading from STDIN.
|
164
|
+
test_files:
|
165
|
+
- spec/gista/gist_request_spec.rb
|
166
|
+
- spec/gista/login_prompt_spec.rb
|
167
|
+
- spec/gista/options_spec.rb
|
168
|
+
- spec/gista/token_request_spec.rb
|
169
|
+
- spec/gista/user_token_spec.rb
|
170
|
+
- spec/spec_helper.rb
|
171
|
+
- spec/support/api_request_examples.rb
|
172
|
+
has_rdoc:
|