gdsh 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +1 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +1 -0
- data/bin/gdsh +10 -0
- data/document.txt +2 -0
- data/gdsh.gemspec +28 -0
- data/lib/gdsh.rb +61 -0
- data/lib/gdsh/clear.rb +20 -0
- data/lib/gdsh/command_factory.rb +32 -0
- data/lib/gdsh/command_mixin.rb +46 -0
- data/lib/gdsh/commands.rb +53 -0
- data/lib/gdsh/drive.rb +113 -0
- data/lib/gdsh/error.rb +7 -0
- data/lib/gdsh/get_file.rb +65 -0
- data/lib/gdsh/help.rb +20 -0
- data/lib/gdsh/list_files.rb +78 -0
- data/lib/gdsh/mime.rb +52 -0
- data/lib/gdsh/query_revision.rb +106 -0
- data/lib/gdsh/quit.rb +23 -0
- data/lib/gdsh/remove.rb +34 -0
- data/lib/gdsh/revision_diff.rb +87 -0
- data/lib/gdsh/share.rb +65 -0
- data/lib/gdsh/share_read_only.rb +18 -0
- data/lib/gdsh/share_read_write.rb +18 -0
- data/lib/gdsh/unrecognized.rb +12 -0
- data/lib/gdsh/upload_template.rb +52 -0
- data/lib/gdsh/version.rb +6 -0
- data/spec/commands_spec.rb +41 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea4c9787d401d716741f4db5606ddc739b16a8f9
|
4
|
+
data.tar.gz: 3f7a0aada9445817f3c3efdaaba3e3ddc6425ed8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 482ad86b93d60e9a832485aad59ccfd86043e60e366e594a086cab8350bcdfcbfe96b8ca714995ee570441a37753771890ef4c8b5e2650db9bfea90692cb325d
|
7
|
+
data.tar.gz: e0acf4d5cc78838575181a6b38164db73c165f150748f41fb30eb8b632dbeec54f1b2aa05dc430ebd1c54bce1f59f1c81d48ae3b93a129e2db1822e28d824239
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
auth.json
|
2
|
+
.session.yaml
|
3
|
+
|
4
|
+
# Bundle generated
|
5
|
+
*.gem
|
6
|
+
*.rbc
|
7
|
+
.bundle
|
8
|
+
.config
|
9
|
+
.yardoc
|
10
|
+
Gemfile.lock
|
11
|
+
installedfiles
|
12
|
+
_yardoc
|
13
|
+
coverage
|
14
|
+
doc/
|
15
|
+
lib/bundler/man
|
16
|
+
pkg
|
17
|
+
rdoc
|
18
|
+
spec/reports
|
19
|
+
test/tmp
|
20
|
+
test/version_tmp
|
21
|
+
tmp
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Emin Tham
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# gdsh [](https://codeclimate.com/github/emintham/gdsh)
|
2
|
+
|
3
|
+
A Google Drive shell in Ruby.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'gdsh'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install gdsh
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
(adapted from https://developers.google.com/drive/web/quickstart/quickstart-ruby)
|
22
|
+
|
23
|
+
1. Set up a project in [Google](https://console.developers.google.com//start/api?id=drive&credential=client_key).
|
24
|
+
2. Select the created project and select APIs & auth and make sure the status
|
25
|
+
is **ON** for the Drive API.
|
26
|
+
3. In the sidebar on the left, select Credentials and set up new credentials
|
27
|
+
for this project.
|
28
|
+
4. Download (as JSON) or save the client id and client secret.
|
29
|
+
5. `bundle exec ruby gdsh.rb <credentials_json>` or `bundle exec ruby gdsh.rb`. In the former, credentials
|
30
|
+
will be automatically retrieved from JSON; in the latter, you will need to
|
31
|
+
copy and paste the `client_id` and `client_secret` from the credentials above.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/gdsh
ADDED
data/document.txt
ADDED
data/gdsh.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gdsh/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'gdsh'
|
8
|
+
spec.version = Gdsh::VERSION
|
9
|
+
spec.authors = ['Emin Tham']
|
10
|
+
spec.email = ['emintham@gmail.com']
|
11
|
+
spec.summary = 'A Google Drive shell in Ruby.'
|
12
|
+
spec.description = ''
|
13
|
+
spec.homepage = 'https://github.com/emintham/gdsh'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_runtime_dependency 'google-api-client'
|
25
|
+
spec.add_runtime_dependency 'launchy'
|
26
|
+
spec.add_runtime_dependency 'differ'
|
27
|
+
spec.add_runtime_dependency 'colorize'
|
28
|
+
end
|
data/lib/gdsh.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'google/api_client'
|
2
|
+
require 'launchy'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require_relative 'gdsh/version'
|
6
|
+
require_relative 'gdsh/drive'
|
7
|
+
require_relative 'gdsh/command_factory'
|
8
|
+
|
9
|
+
##
|
10
|
+
# Implements a command interpreter to wrap Google Drive API.
|
11
|
+
#
|
12
|
+
module Gdsh
|
13
|
+
##
|
14
|
+
# Gdsh Class
|
15
|
+
#
|
16
|
+
class Gdsh < DriveService
|
17
|
+
include CommandFactory
|
18
|
+
|
19
|
+
def puts_banner
|
20
|
+
puts ''
|
21
|
+
puts 'CLI tool to interface with Google Drive'.colorize(:green)
|
22
|
+
puts '======================================='.colorize(:green)
|
23
|
+
end
|
24
|
+
|
25
|
+
def puts_hint
|
26
|
+
puts 'Hint: type \'help\'.'.colorize(:green)
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear_screen
|
30
|
+
system('clear') || system('cls')
|
31
|
+
end
|
32
|
+
|
33
|
+
def init_shell
|
34
|
+
puts_banner
|
35
|
+
authorize
|
36
|
+
clear_screen
|
37
|
+
puts_hint
|
38
|
+
end
|
39
|
+
|
40
|
+
def prints_prompt
|
41
|
+
print 'gdsh> '.colorize(:light_blue)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Command interpreter
|
46
|
+
#
|
47
|
+
def shell
|
48
|
+
init_shell
|
49
|
+
|
50
|
+
loop do
|
51
|
+
prints_prompt
|
52
|
+
params = parsed_inputs
|
53
|
+
command = next_command(params)
|
54
|
+
command.new(@client, params).execute
|
55
|
+
break if command.terminal?
|
56
|
+
end
|
57
|
+
|
58
|
+
write_session_info_to_file
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/gdsh/clear.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'commands'
|
2
|
+
|
3
|
+
module Commands
|
4
|
+
##
|
5
|
+
# Clear screen.
|
6
|
+
#
|
7
|
+
class Clear < Command
|
8
|
+
def self.command_name
|
9
|
+
'clear'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.function
|
13
|
+
'Clears the screen.'
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
system('clear') || system('cls')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'commands'
|
2
|
+
require_relative 'clear'
|
3
|
+
require_relative 'help'
|
4
|
+
require_relative 'list_files'
|
5
|
+
require_relative 'query_revision'
|
6
|
+
require_relative 'quit'
|
7
|
+
require_relative 'unrecognized'
|
8
|
+
require_relative 'upload_template'
|
9
|
+
require_relative 'get_file'
|
10
|
+
require_relative 'revision_diff'
|
11
|
+
require_relative 'remove'
|
12
|
+
require_relative 'share_read_only'
|
13
|
+
require_relative 'share_read_write'
|
14
|
+
|
15
|
+
##
|
16
|
+
# Factory pattern to create the appropriate command.
|
17
|
+
#
|
18
|
+
module CommandFactory
|
19
|
+
include Commands
|
20
|
+
|
21
|
+
def parsed_inputs
|
22
|
+
# gets user input, defaulting to empty and removing newline
|
23
|
+
user_input = $stdin.gets || ''
|
24
|
+
|
25
|
+
# if command has parameters, split it up
|
26
|
+
user_input.chomp.split(/[\(,\),\,,\ ]/)
|
27
|
+
end
|
28
|
+
|
29
|
+
def next_command(params)
|
30
|
+
Commands.interpret(params[0])
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Commands
|
5
|
+
#
|
6
|
+
module Commands
|
7
|
+
##
|
8
|
+
# Command mixin
|
9
|
+
#
|
10
|
+
module CommandMixin
|
11
|
+
def execute
|
12
|
+
fail 'Method not implemented.'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Every 'useful' Command class should override the following
|
21
|
+
# (except terminal?)
|
22
|
+
#
|
23
|
+
module ClassMethods
|
24
|
+
def command_name
|
25
|
+
''
|
26
|
+
end
|
27
|
+
|
28
|
+
def function
|
29
|
+
''
|
30
|
+
end
|
31
|
+
|
32
|
+
def parameters
|
33
|
+
''
|
34
|
+
end
|
35
|
+
|
36
|
+
def description
|
37
|
+
(command_name + parameters + ":\n ").colorize(:green) +
|
38
|
+
function.colorize(:yellow)
|
39
|
+
end
|
40
|
+
|
41
|
+
def terminal?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'google/api_client'
|
2
|
+
require 'json'
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
require_relative 'command_mixin'
|
6
|
+
require_relative 'error'
|
7
|
+
|
8
|
+
##
|
9
|
+
# Commands
|
10
|
+
#
|
11
|
+
module Commands
|
12
|
+
##
|
13
|
+
# Command with an initialized client object.
|
14
|
+
#
|
15
|
+
class Command
|
16
|
+
include CommandMixin
|
17
|
+
include DriveError
|
18
|
+
|
19
|
+
def initialize(client, params)
|
20
|
+
@client, @params = client, params
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module_function
|
25
|
+
|
26
|
+
def commands
|
27
|
+
constants.select { |c| const_get(c).is_a? Class }
|
28
|
+
end
|
29
|
+
|
30
|
+
def puts_usage_header
|
31
|
+
puts 'Commands'.colorize(:green)
|
32
|
+
puts '--------'.colorize(:green)
|
33
|
+
end
|
34
|
+
|
35
|
+
def usage
|
36
|
+
puts_usage_header
|
37
|
+
commands.each do |c|
|
38
|
+
klass = const_get(c)
|
39
|
+
puts klass.description unless klass.command_name.empty?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def interpret(input)
|
44
|
+
return Quit if input.nil?
|
45
|
+
|
46
|
+
commands.each do |c|
|
47
|
+
klass = const_get(c)
|
48
|
+
return klass if input == klass.command_name
|
49
|
+
end
|
50
|
+
|
51
|
+
Unrecognized
|
52
|
+
end
|
53
|
+
end
|
data/lib/gdsh/drive.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'google/api_client'
|
2
|
+
require 'launchy'
|
3
|
+
require 'json'
|
4
|
+
require 'colorize'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
##
|
8
|
+
# DriveService module implements a service that interfaces with
|
9
|
+
# Google Drive using Google Drive API.
|
10
|
+
#
|
11
|
+
class DriveService
|
12
|
+
##
|
13
|
+
# Creates a new Google Drive Shell object.
|
14
|
+
#
|
15
|
+
# @param [String] filename
|
16
|
+
# filename of json containing credentials downloaded from Google.
|
17
|
+
#
|
18
|
+
def initialize(filename = nil)
|
19
|
+
# default to per-file permissions
|
20
|
+
@oauth_scope = 'https://www.googleapis.com/auth/drive.file'
|
21
|
+
@redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
|
22
|
+
@filename = filename
|
23
|
+
|
24
|
+
if filename && File.exist?(filename)
|
25
|
+
credentials_from_file
|
26
|
+
else
|
27
|
+
credentials_from_stdin
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def credentials_from_file
|
32
|
+
File.open(@filename, 'r') do |f|
|
33
|
+
buffer = f.read
|
34
|
+
credentials = JSON.parse(buffer)
|
35
|
+
@client_id = credentials['installed']['client_id']
|
36
|
+
@client_secret = credentials['installed']['client_secret']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def init_client
|
41
|
+
# Create a new API client & load the Google Drive API
|
42
|
+
@client = Google::APIClient.new
|
43
|
+
|
44
|
+
# Request authorization
|
45
|
+
@client.authorization.client_id = @client_id
|
46
|
+
@client.authorization.client_secret = @client_secret
|
47
|
+
@client.authorization.scope = @oauth_scope
|
48
|
+
@client.authorization.redirect_uri = @redirect_uri
|
49
|
+
end
|
50
|
+
|
51
|
+
def puts_refresh_error
|
52
|
+
puts 'Could not refresh token from saved session.'.colorize(:red)
|
53
|
+
end
|
54
|
+
|
55
|
+
def authorize
|
56
|
+
init_client
|
57
|
+
|
58
|
+
begin
|
59
|
+
authorize_from_refresh_token
|
60
|
+
rescue
|
61
|
+
puts_refresh_error
|
62
|
+
authorize_from_authorization_code
|
63
|
+
ensure
|
64
|
+
@client.authorization.fetch_access_token!
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def authorize_from_refresh_token
|
69
|
+
fail StandardError unless File.exist?('.session.yaml')
|
70
|
+
|
71
|
+
f = File.open('.session.yaml', 'r')
|
72
|
+
session = YAML.load(f.read)
|
73
|
+
@client.authorization.grant_type = 'refresh_token'
|
74
|
+
@client.authorization.refresh_token = session.authorization.refresh_token
|
75
|
+
f.close
|
76
|
+
end
|
77
|
+
|
78
|
+
def authorize_from_authorization_code
|
79
|
+
uri = @client.authorization.authorization_uri
|
80
|
+
Launchy.open(uri)
|
81
|
+
|
82
|
+
# Exchange authorization code for access token
|
83
|
+
print 'Enter authorization code: '.colorize(:light_blue)
|
84
|
+
@client.authorization.code = $stdin.gets.chomp
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Get credentials from shell if no credentials file was specified.
|
89
|
+
#
|
90
|
+
def credentials_from_stdin
|
91
|
+
# get preset if exists
|
92
|
+
@client_id ||= ''
|
93
|
+
@client_secret ||= ''
|
94
|
+
|
95
|
+
# Ask from user otherwise
|
96
|
+
if @client_id == ''
|
97
|
+
print 'Please enter your client id: '
|
98
|
+
@client_id = $stdin.gets.chomp
|
99
|
+
end
|
100
|
+
|
101
|
+
if @client_secret == ''
|
102
|
+
print 'Please enter your client secret: '
|
103
|
+
@client_secret = $stdin.gets.chomp
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def write_session_info_to_file
|
108
|
+
return if @client.nil?
|
109
|
+
f = File.new('.session.yaml', 'w')
|
110
|
+
f.write(@client.to_yaml)
|
111
|
+
f.close
|
112
|
+
end
|
113
|
+
end
|