google_drive_companion 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +82 -0
- data/Rakefile +12 -0
- data/bin/gddel +13 -0
- data/bin/gdmv +13 -0
- data/bin/gdpull +20 -0
- data/bin/gdpush +19 -0
- data/bin/gdrive +18 -0
- data/google_drive_companion.gemspec +23 -0
- data/lib/google_drive_companion.rb +15 -0
- data/lib/google_drive_companion/server.rb +96 -0
- data/lib/google_drive_companion/session.rb +202 -0
- data/lib/google_drive_companion/socket.rb +27 -0
- data/lib/google_drive_companion/version.rb +3 -0
- data/lib/help.txt +23 -0
- data/test/server_test.rb +102 -0
- data/test/session_test.rb +165 -0
- data/test/socket_test.rb +38 -0
- data/test/test_helper.rb +5 -0
- metadata +123 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Edward Price
|
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,82 @@
|
|
1
|
+
# GoogleDriveCompanion
|
2
|
+
|
3
|
+
I was playing with the "google-drive-ruby" gem and thought it was awesome.
|
4
|
+
|
5
|
+
I don't like leaving the terminal, so I thought, meh, I am going to write a gem that allows me to push from the command line.
|
6
|
+
|
7
|
+
Then, I figured: "Yo!" The handshake takes to long, so why not set of a unix server socket to run as a daemon, listening for my google drive commands so that it will go faster.
|
8
|
+
|
9
|
+
Sweetness!
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'google_drive_companion'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install google_drive_companion
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Get help
|
28
|
+
|
29
|
+
gdrive help
|
30
|
+
|
31
|
+
Start the server using environment variables
|
32
|
+
|
33
|
+
username=AzureDiamond@gmail.com password=hunter2 gdrive start
|
34
|
+
|
35
|
+
Or create a file in your ~/.google_drive/conf.yaml:
|
36
|
+
|
37
|
+
username: AzureDiamond@gmail.com
|
38
|
+
password: hunter2
|
39
|
+
|
40
|
+
Toggle the foreground/daemon flag when you start the server (def. as a daemon):
|
41
|
+
|
42
|
+
gdrive start [run_in_foreground]
|
43
|
+
|
44
|
+
Upload a file:
|
45
|
+
|
46
|
+
gdpush /path/to/file.txt
|
47
|
+
|
48
|
+
Upload a file from the current dir:
|
49
|
+
|
50
|
+
gdpush this.txt
|
51
|
+
|
52
|
+
Upload a file from the current dir, to a remote location:
|
53
|
+
|
54
|
+
gdpush this.txt remote/path/to/that.txt
|
55
|
+
|
56
|
+
Move a remote file to another remote folder:
|
57
|
+
|
58
|
+
gdmv /path/to/file.txt /another/path
|
59
|
+
|
60
|
+
Download the new file:
|
61
|
+
|
62
|
+
gdpull /another/path/file.txt
|
63
|
+
|
64
|
+
Trash the file:
|
65
|
+
|
66
|
+
gddel /another/path/file.txt
|
67
|
+
|
68
|
+
Trash whole folders, with reckless abandon!
|
69
|
+
|
70
|
+
gddel another
|
71
|
+
|
72
|
+
Kill the server:
|
73
|
+
|
74
|
+
gdrive stop
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
1. Fork it
|
79
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
80
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
81
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
82
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/gddel
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "google_drive_companion"
|
3
|
+
|
4
|
+
begin
|
5
|
+
res = GoogleDriveCompanion::Socket.instance
|
6
|
+
$stdout.puts res.digest(["del"] + ARGV)
|
7
|
+
|
8
|
+
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
9
|
+
$stdout.puts <<EOS
|
10
|
+
Error connecting to server, please try again after running:
|
11
|
+
$ gdrive start
|
12
|
+
EOS
|
13
|
+
end
|
data/bin/gdmv
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "google_drive_companion"
|
3
|
+
|
4
|
+
begin
|
5
|
+
res = GoogleDriveCompanion::Socket.instance
|
6
|
+
$stdout.puts res.digest(["mv"] + ARGV)
|
7
|
+
|
8
|
+
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
9
|
+
$stdout.puts <<EOS
|
10
|
+
Error connecting to server, please try again after running:
|
11
|
+
$ gdrive start
|
12
|
+
EOS
|
13
|
+
end
|
data/bin/gdpull
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "google_drive_companion"
|
3
|
+
|
4
|
+
begin
|
5
|
+
res = GoogleDriveCompanion::Socket.instance
|
6
|
+
|
7
|
+
# If it ain't an absolute path, make it so!
|
8
|
+
ARGV[1] ||= GoogleDriveCompanion::Session.split_file(ARGV[0]).last
|
9
|
+
unless ARGV[1][/^\//]
|
10
|
+
ARGV[1] = File.join(Dir.pwd, ARGV[1])
|
11
|
+
end
|
12
|
+
|
13
|
+
$stdout.puts res.digest(["pull"] + ARGV)
|
14
|
+
|
15
|
+
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
16
|
+
$stdout.puts <<EOS
|
17
|
+
Error connecting to server, please try again after running:
|
18
|
+
$ gdrive start
|
19
|
+
EOS
|
20
|
+
end
|
data/bin/gdpush
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "google_drive_companion"
|
3
|
+
|
4
|
+
begin
|
5
|
+
res = GoogleDriveCompanion::Socket.instance
|
6
|
+
|
7
|
+
# If it ain't an absolute path, make it so!
|
8
|
+
unless ARGV[0][/^\//]
|
9
|
+
ARGV[0] = File.join(Dir.pwd, ARGV[0])
|
10
|
+
end
|
11
|
+
|
12
|
+
$stdout.puts res.digest(["push"] + ARGV)
|
13
|
+
|
14
|
+
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
15
|
+
$stdout.puts <<EOS
|
16
|
+
Error connecting to server, please try again after running:
|
17
|
+
$ gdrive start
|
18
|
+
EOS
|
19
|
+
end
|
data/bin/gdrive
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "google_drive_companion"
|
3
|
+
|
4
|
+
case ARGV[0].downcase
|
5
|
+
when "start"
|
6
|
+
GoogleDriveCompanion::Server.instance.run! ARGV[1]
|
7
|
+
else
|
8
|
+
begin
|
9
|
+
instance = GoogleDriveCompanion::Socket.instance
|
10
|
+
puts instance.digest(ARGV)
|
11
|
+
instance.close
|
12
|
+
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
13
|
+
$stdout.puts <<EOS
|
14
|
+
Error connecting to server, please try again after running:
|
15
|
+
$ gdrive start
|
16
|
+
EOS
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'google_drive_companion/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "google_drive_companion"
|
8
|
+
gem.version = GoogleDriveCompanion::VERSION
|
9
|
+
gem.authors = ["Edward Price"]
|
10
|
+
gem.email = ["ted.price+github@gmail.com"]
|
11
|
+
gem.description = %q{Interface with google drive from tha matrix}
|
12
|
+
gem.summary = %q{Interface with google drive from tha matrix}
|
13
|
+
gem.homepage = "https://github.com/pricees/google-drive-companion"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency("google_drive")
|
21
|
+
gem.add_development_dependency("minitest")
|
22
|
+
gem.add_development_dependency("mocha")
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "singleton"
|
3
|
+
require "fileutils"
|
4
|
+
require "yaml"
|
5
|
+
require "google_drive"
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
module GoogleDriveCompanion
|
9
|
+
# Your code goes here...
|
10
|
+
end
|
11
|
+
|
12
|
+
require_relative "google_drive_companion/version"
|
13
|
+
require_relative "google_drive_companion/server"
|
14
|
+
require_relative "google_drive_companion/session"
|
15
|
+
require_relative "google_drive_companion/socket"
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# This module sets up a unix socket daemon process to listen out for commands
|
2
|
+
module GoogleDriveCompanion
|
3
|
+
class Server
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
def socket_file
|
7
|
+
ENV["gdc_socket"] || File.join("", "tmp", "gdc_socket.sock")
|
8
|
+
end
|
9
|
+
|
10
|
+
def pid_file
|
11
|
+
ENV["gdc_pid"] || File.join("", "tmp", "gdc.pid")
|
12
|
+
end
|
13
|
+
|
14
|
+
def check_if_running!
|
15
|
+
if File.exists?(socket_file)
|
16
|
+
puts "Socket file #{socket_file} in use"
|
17
|
+
leave = true
|
18
|
+
end
|
19
|
+
|
20
|
+
if File.exists?(pid_file)
|
21
|
+
pid = File.read(pid_file)
|
22
|
+
puts "Server may be running on #{pid}\n**************\n"
|
23
|
+
leave ||= true
|
24
|
+
end
|
25
|
+
leave && exit(1)
|
26
|
+
end
|
27
|
+
|
28
|
+
def server
|
29
|
+
@server ||= begin
|
30
|
+
check_if_running!
|
31
|
+
write(Process.pid, pid_file)
|
32
|
+
UNIXServer.new(socket_file)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def close
|
37
|
+
server.close
|
38
|
+
end
|
39
|
+
|
40
|
+
def close!
|
41
|
+
close
|
42
|
+
File.exists?(socket_file) && FileUtils.rm(socket_file)
|
43
|
+
File.exists?(pid_file) && FileUtils.rm(pid_file)
|
44
|
+
Process.kill 9, Process.pid
|
45
|
+
end
|
46
|
+
|
47
|
+
# Attempts to write the pid of the forked process to the pid file.
|
48
|
+
# Kills process if write unsuccesfull.
|
49
|
+
def write(pid, pidfile)
|
50
|
+
File.open pidfile, "w" do |f|
|
51
|
+
f.write pid
|
52
|
+
f.close
|
53
|
+
end
|
54
|
+
$stdout.puts "Daemon running with pid: #{pid}"
|
55
|
+
rescue ::Exception => e
|
56
|
+
raise "While writing the PID to file, unexpected #{e.class}: #{e}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def run!(arg = nil)
|
60
|
+
server
|
61
|
+
Process.daemon unless arg
|
62
|
+
msg_pump.join
|
63
|
+
end
|
64
|
+
|
65
|
+
def respond(s, msg)
|
66
|
+
s.send(msg, 0)
|
67
|
+
rescue Errno::EPIPE
|
68
|
+
$stderr.puts "Socket closed, meh"
|
69
|
+
end
|
70
|
+
|
71
|
+
def msg_pump
|
72
|
+
loop do
|
73
|
+
begin
|
74
|
+
s = server.accept
|
75
|
+
while (cmd = s.recv(1000))
|
76
|
+
cmd = JSON.parse(cmd)
|
77
|
+
case cmd.first
|
78
|
+
when "stop"
|
79
|
+
close!
|
80
|
+
when "help"
|
81
|
+
help_file = File.join(File.dirname(__FILE__), "..", "help.txt")
|
82
|
+
respond(s, File.read(help_file))
|
83
|
+
else
|
84
|
+
GoogleDriveCompanion::Session.handle_msg(cmd)
|
85
|
+
respond(s, "[#{cmd.first}] Success!")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
rescue Exception => bang
|
89
|
+
tmp = "Server error: #{bang}"
|
90
|
+
$stderr.puts tmp
|
91
|
+
respond(s, tmp)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module GoogleDriveCompanion
|
2
|
+
|
3
|
+
module Session
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Set authentication creditials in ~/.google_drive/conf.yaml
|
7
|
+
#
|
8
|
+
# ~/.google_drive/conf.yaml:
|
9
|
+
#
|
10
|
+
# username: AzureDiamond@gmail.com
|
11
|
+
# password: hunter2
|
12
|
+
#
|
13
|
+
# OR at the env level:
|
14
|
+
#
|
15
|
+
# username=AzureDiamond@gmail.com password=hunter2 [cmd] [arg1, ... ]
|
16
|
+
#
|
17
|
+
def configs
|
18
|
+
@configs ||= begin
|
19
|
+
fn = File.join(Dir.home, ".google_drive", "conf.yaml")
|
20
|
+
configs = File.exists?(fn) ? YAML::load(File.open(fn)) : ENV
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Split filename into array by separator
|
25
|
+
#
|
26
|
+
# name - Name of file
|
27
|
+
# sep - Seperator (def. system separator)
|
28
|
+
#
|
29
|
+
# Examples
|
30
|
+
#
|
31
|
+
# split_file("/path/to/file.txt")
|
32
|
+
# # => ["path", "to", "file.txt")
|
33
|
+
#
|
34
|
+
# split_file("-path-to-file.txt----", "-")
|
35
|
+
# res.must_equal(exp)
|
36
|
+
#
|
37
|
+
# Returns array
|
38
|
+
def split_file(name = "", sep = File::SEPARATOR)
|
39
|
+
name.gsub(/^#{sep}+/, '').split(sep)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Return the top level folder of the accounts google drive
|
43
|
+
#
|
44
|
+
# Returns GoogleDrive::Collection
|
45
|
+
def root
|
46
|
+
session.root_collection
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Call methods, check against whitelist
|
50
|
+
#
|
51
|
+
#
|
52
|
+
# Returns [something]
|
53
|
+
PROTECTED_METHODS = %w[push pull mv del]
|
54
|
+
def send_protected(ary)
|
55
|
+
if PROTECTED_METHODS.include?(ary.first)
|
56
|
+
send(ary.shift.to_sym, *ary)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Takes an array of names of gdrive folders and traverss them. If name is erroneous, it stops at last leaf; if forced, creates the subcollection
|
61
|
+
#
|
62
|
+
# Examples
|
63
|
+
#
|
64
|
+
# traverse(["foo", "bar"])
|
65
|
+
# # => <collection "bar">
|
66
|
+
#
|
67
|
+
# traverse(["foo", "bar", "baz"]) NOTE: bar doesn't exist
|
68
|
+
# # => <collection "foo">
|
69
|
+
#
|
70
|
+
# traverse(["foo", "bar", "baz"], :force) NOTE: bar doesn't exist
|
71
|
+
# # => <collection "baz">
|
72
|
+
#
|
73
|
+
# Returns a GoogleDrive::Collection
|
74
|
+
def traverse(collections, force = false)
|
75
|
+
node = root
|
76
|
+
collections.each do |subcollection|
|
77
|
+
new_node = node.subcollection_by_title(subcollection)
|
78
|
+
|
79
|
+
if new_node
|
80
|
+
node = new_node
|
81
|
+
elsif force
|
82
|
+
node = node.create_subcollection(subcollection)
|
83
|
+
else
|
84
|
+
break
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
node
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: Get a file from Google Drive
|
92
|
+
#
|
93
|
+
# ary - Array of collections, last element is title of file
|
94
|
+
#
|
95
|
+
# Example
|
96
|
+
#
|
97
|
+
# get_file(["foo", "bar", "faz.rb"])
|
98
|
+
# # => <GoogleDrive::File faz.rb>
|
99
|
+
#
|
100
|
+
# Returns a GoogleDrive::File
|
101
|
+
def get_file(ary)
|
102
|
+
title = ary.pop
|
103
|
+
traverse(ary).
|
104
|
+
files({ "title" => title, "title-exact" => "true" })[0]
|
105
|
+
end
|
106
|
+
|
107
|
+
# Public: Move file from one remote location to another
|
108
|
+
#
|
109
|
+
# src - Source remote path to file
|
110
|
+
# dest - Destination remote path for file
|
111
|
+
# force - Make destination folder path if not already (def. true)
|
112
|
+
# Examples
|
113
|
+
#
|
114
|
+
# mv("/old/path/to/file.txt", "/new/path/")
|
115
|
+
# # => mil;
|
116
|
+
#
|
117
|
+
# Raise RuntimeError is remote file doesn't exist
|
118
|
+
# Returns nil
|
119
|
+
def mv(src, dest, force = true)
|
120
|
+
src_ary = split_file(src)
|
121
|
+
src_node = traverse(src_ary[0...-1])
|
122
|
+
file = get_file(src_ary)
|
123
|
+
|
124
|
+
if file
|
125
|
+
traverse(split_file(dest), force).add(file)
|
126
|
+
src_node.remove(file)
|
127
|
+
else
|
128
|
+
raise "Remote file: #{file} doesn't exist yo!"
|
129
|
+
end
|
130
|
+
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# Public: Uploads local file to remote local
|
135
|
+
#
|
136
|
+
# remote_file - Remote file path, File::SEPARATOR delimited
|
137
|
+
# local_file - Local file path, File::SEPARATOR delimited
|
138
|
+
#
|
139
|
+
# Examples
|
140
|
+
#
|
141
|
+
# push("/tmp/text.txt")
|
142
|
+
# # => [ remote file /tmp/text.txt ]
|
143
|
+
#
|
144
|
+
# push("/tmp/text.txt", "/remote/path/to/text.txt")
|
145
|
+
# # => [ remote file /remote/path/to/text.txt ]
|
146
|
+
#
|
147
|
+
# Returns GoogleDrive::File
|
148
|
+
def push(local_file, remote_file = nil, force = true)
|
149
|
+
|
150
|
+
remote_file ||= local_file
|
151
|
+
ary = split_file(remote_file)
|
152
|
+
remote_fn = ary.pop
|
153
|
+
file = session.upload_from_file(local_file, remote_fn)
|
154
|
+
|
155
|
+
unless ary.empty?
|
156
|
+
traverse(ary, force).add(file)
|
157
|
+
root.remove(file)
|
158
|
+
end
|
159
|
+
file
|
160
|
+
end
|
161
|
+
|
162
|
+
# Public: Downloads remote file
|
163
|
+
#
|
164
|
+
# remote_file - Remote file path, File::SEPARATOR delimited
|
165
|
+
# local_file - Local file path, File::SEPARATOR delimited
|
166
|
+
#
|
167
|
+
# Examples
|
168
|
+
#
|
169
|
+
# pull("/remote/path/to/text.txt", "/tmp/text.txt")
|
170
|
+
# # => [ file /tmp/text.txt ]
|
171
|
+
#
|
172
|
+
# Returns an integer
|
173
|
+
def pull(remote_file, local_file = nil)
|
174
|
+
|
175
|
+
remote_ary = split_file(remote_file)
|
176
|
+
local_file ||= remote_ary.last
|
177
|
+
|
178
|
+
get_file(remote_ary).download_to_file(local_file)
|
179
|
+
rescue
|
180
|
+
raise "problem locating remote file: #{remote_file}"
|
181
|
+
end
|
182
|
+
|
183
|
+
# Public: Deletes remote folder
|
184
|
+
#
|
185
|
+
# file - Remote file path, File::SEPARATOR delimited
|
186
|
+
# permanent - Delete permantantly? (or just put in trash) (def. false)
|
187
|
+
#
|
188
|
+
# Returns boolean
|
189
|
+
def del(file, permanently = false)
|
190
|
+
get_file(split_file(file)).delete(permanently)
|
191
|
+
permanently
|
192
|
+
end
|
193
|
+
|
194
|
+
def handle_msg(cmd)
|
195
|
+
send_protected(cmd) || "bad command!"
|
196
|
+
end
|
197
|
+
|
198
|
+
def session
|
199
|
+
@session = GoogleDrive.login(configs["username"], configs["password"])
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Class: The class connects to the UNIX socket and pushes the command line as quickly as possible
|
2
|
+
#
|
3
|
+
# NOTE: I didn't comment this section, because the code is so amazing, sometimes I just cry looking at it.
|
4
|
+
#
|
5
|
+
# Oops there it goes, a tear drop right in my Portillo's Italian Sausage.
|
6
|
+
module GoogleDriveCompanion
|
7
|
+
class Socket
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def socket_file
|
11
|
+
ENV["gdc_socket"] || File.join("", "tmp", "gdc_socket.sock")
|
12
|
+
end
|
13
|
+
|
14
|
+
def socket
|
15
|
+
@socket ||= UNIXSocket.open(socket_file)
|
16
|
+
end
|
17
|
+
|
18
|
+
def close
|
19
|
+
socket.close
|
20
|
+
end
|
21
|
+
|
22
|
+
def digest(msg)
|
23
|
+
socket.send(msg.to_json, 0)
|
24
|
+
socket.recv(10_000)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/help.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
======================
|
2
|
+
Google Drive Companion
|
3
|
+
======================
|
4
|
+
|
5
|
+
gdriver s # start server
|
6
|
+
|
7
|
+
== Upload Files ==
|
8
|
+
gdpush [local file] [remote file]
|
9
|
+
gdpush /tmp/foo remote/path/bar # push local file /tmp/foo -> remote/path/bar
|
10
|
+
gdpush /tmp/foo [remote file] # push local file /tmp/foo -> remote /tmp/foo
|
11
|
+
gdpush relative.txt [remote file] # push file in current directory, will retain directory structure if remote file unspecified, beware!
|
12
|
+
|
13
|
+
== Download Files ==
|
14
|
+
gdpull /remote/path/bar.txt # Download to the relative path (./bar.txt)
|
15
|
+
gdpull /remote/path/bar.txt /tmp/foo.txt # Download to absoltute path and filename
|
16
|
+
|
17
|
+
== Move Files ==
|
18
|
+
gdmv /foo/bar.txt /new/path # push remote file to different directory (/foo/bar.txt - > new/path/bar.txt)
|
19
|
+
|
20
|
+
== Remove Remote Files ==
|
21
|
+
gddel /new/path/bar.txt # Delete a file
|
22
|
+
gddel /new/ # Delete the whole directory (AHHHHHHHHHH!)
|
23
|
+
|
data/test/server_test.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require_relative "test_helper.rb"
|
2
|
+
|
3
|
+
describe GoogleDriveCompanion::Server do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@instance = GoogleDriveCompanion::Server.instance
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns socket filename" do
|
10
|
+
@instance.socket_file.must_equal "/tmp/gdc_socket.sock"
|
11
|
+
|
12
|
+
ENV["gdc_socket"] = "/foo"
|
13
|
+
@instance.socket_file.must_equal "/foo"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns pid filename" do
|
17
|
+
@instance.pid_file.must_equal "/tmp/gdc.pid"
|
18
|
+
|
19
|
+
ENV["gdc_pid"] = "/foo"
|
20
|
+
@instance.pid_file.must_equal "/foo"
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "checks if the server is running" do
|
24
|
+
|
25
|
+
it "may be running because of the unix socket file" do
|
26
|
+
@instance.expects(:socket_file).times(2).returns(:foo)
|
27
|
+
@instance.expects(:pid_file).returns(:bar)
|
28
|
+
|
29
|
+
File.expects(:exists?).with(:foo).returns(true)
|
30
|
+
File.expects(:exists?).with(:bar).returns(false)
|
31
|
+
|
32
|
+
@instance.expects(:exit).with(1)
|
33
|
+
refute @instance.check_if_running!
|
34
|
+
end
|
35
|
+
|
36
|
+
it "may be running because of the pidfile" do
|
37
|
+
@instance.expects(:socket_file).returns(:foo)
|
38
|
+
@instance.expects(:pid_file).times(2).returns(:bar)
|
39
|
+
|
40
|
+
File.expects(:exists?).with(:foo).returns(false)
|
41
|
+
File.expects(:exists?).with(:bar).returns(true)
|
42
|
+
File.expects(:read).with(:bar)
|
43
|
+
|
44
|
+
@instance.expects(:exit).with(1)
|
45
|
+
refute @instance.check_if_running!
|
46
|
+
end
|
47
|
+
|
48
|
+
it "isn't running" do
|
49
|
+
@instance.expects(:socket_file).returns(:foo)
|
50
|
+
@instance.expects(:pid_file).returns(:bar)
|
51
|
+
|
52
|
+
File.expects(:exists?).with(:foo).returns(false)
|
53
|
+
File.expects(:exists?).with(:bar).returns(false)
|
54
|
+
|
55
|
+
@instance.expects(:exit).times(0)
|
56
|
+
refute @instance.check_if_running!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "grabs a new unix socket it appropriate" do
|
61
|
+
@instance.expects(:check_if_running!)
|
62
|
+
@instance.expects(:write)
|
63
|
+
@instance.expects(:socket_file).returns(:socket!)
|
64
|
+
UNIXServer.expects(:new).with(:socket!).returns(:success!)
|
65
|
+
|
66
|
+
@instance.server.must_equal(:success!)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "closes its socket" do
|
70
|
+
@instance.expects(:server).returns(mock(close: :success!))
|
71
|
+
@instance.close.must_equal(:success!)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "kills the server" do
|
75
|
+
@instance.expects(:close)
|
76
|
+
@instance.expects(:socket_file).times(2).returns(:yar!)
|
77
|
+
@instance.expects(:pid_file).times(2).returns(:she_blows!)
|
78
|
+
|
79
|
+
File.expects(:exists?).with(:yar!).returns(true)
|
80
|
+
File.expects(:exists?).with(:she_blows!).returns(true)
|
81
|
+
FileUtils.expects(:rm).times(2)
|
82
|
+
Process.expects(:kill)
|
83
|
+
|
84
|
+
@instance.close!
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "runs the server" do
|
88
|
+
it "as a daemon process" do
|
89
|
+
@instance.expects(:server)
|
90
|
+
Process.expects(:daemon).times(1)
|
91
|
+
@instance.expects(:msg_pump).returns []
|
92
|
+
@instance.run!
|
93
|
+
end
|
94
|
+
|
95
|
+
it "in the foreground" do
|
96
|
+
@instance.expects(:server)
|
97
|
+
Process.expects(:daemon).times(0)
|
98
|
+
@instance.expects(:msg_pump).returns []
|
99
|
+
@instance.run! :foo!
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require_relative "test_helper.rb"
|
2
|
+
|
3
|
+
describe GoogleDriveCompanion::Session do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@module = GoogleDriveCompanion::Session
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "has a config" do
|
10
|
+
|
11
|
+
it "gets from a file" do
|
12
|
+
end
|
13
|
+
|
14
|
+
it "gets from env" do
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "has a session" do
|
19
|
+
hsh = { "username" => "foo", "password" => "bar" }
|
20
|
+
@module.expects(:configs).times(2).returns(hsh)
|
21
|
+
GoogleDrive.expects(:login).with("foo", "bar").returns(:jeah)
|
22
|
+
@module.session.must_equal(:jeah)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "sends methods" do
|
26
|
+
ary = %w[mv bar baz]
|
27
|
+
@module.expects(:send).with(:mv, "bar", "baz")
|
28
|
+
@module.send_protected(ary)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "handles message" do
|
32
|
+
hsh = { "foo" => "bar" }
|
33
|
+
@module.expects(:send_protected).with(hsh)
|
34
|
+
@module.handle_msg(hsh)
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "handles files" do
|
38
|
+
|
39
|
+
it "seperates files by separator" do
|
40
|
+
exp = %w(path to file.txt)
|
41
|
+
res = @module.split_file("path/to/file.txt")
|
42
|
+
res.must_equal(exp)
|
43
|
+
|
44
|
+
res = @module.split_file("/path/to/file.txt")
|
45
|
+
res.must_equal(exp)
|
46
|
+
|
47
|
+
res = @module.split_file("/path/to/file.txt////")
|
48
|
+
res.must_equal(exp)
|
49
|
+
|
50
|
+
res = @module.split_file("-path-to-file.txt----", "-")
|
51
|
+
res.must_equal(exp)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "collections" do
|
56
|
+
|
57
|
+
it "traverses a full collection tree" do
|
58
|
+
root = mock
|
59
|
+
foo = mock
|
60
|
+
bar = mock
|
61
|
+
root.expects(:subcollection_by_title).with("foo").returns(foo)
|
62
|
+
foo.expects(:subcollection_by_title).with("bar").returns(bar)
|
63
|
+
bar.expects(:subcollection_by_title).with("baz").returns(:success!)
|
64
|
+
@module.expects(:root).returns(root)
|
65
|
+
@module.traverse(%w(foo bar baz)).must_equal(:success!)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "stops at leaf" do
|
69
|
+
root = mock
|
70
|
+
foo = mock
|
71
|
+
bar = mock
|
72
|
+
root.expects(:subcollection_by_title).with("foo").returns(foo)
|
73
|
+
foo.expects(:subcollection_by_title).with("bar").returns(nil)
|
74
|
+
@module.expects(:root).returns(root)
|
75
|
+
@module.traverse(%w(foo bar baz)).must_equal(foo)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "traverses a known collection tree" do
|
79
|
+
root = mock
|
80
|
+
foo = mock
|
81
|
+
bar = mock
|
82
|
+
root.expects(:subcollection_by_title).with("foo").returns(foo)
|
83
|
+
foo.expects(:subcollection_by_title).with("bar").returns(nil)
|
84
|
+
foo.expects(:create_subcollection).with("bar").returns(:success!)
|
85
|
+
|
86
|
+
@module.expects(:root).returns(root)
|
87
|
+
@module.traverse(%w(foo bar), :force).must_equal(:success!)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "gets the root 'directory' of the gdrive" do
|
92
|
+
|
93
|
+
@module.expects(:session).returns(mock(root_collection: :success!))
|
94
|
+
@module.root.must_equal :success!
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
describe "handles files on gdrive" do
|
99
|
+
|
100
|
+
it "pushes a file" do
|
101
|
+
m = mock
|
102
|
+
m.expects(:upload_from_file).with("baz.rb", "baz.rb")
|
103
|
+
@module.expects(:session).returns(m)
|
104
|
+
|
105
|
+
ary = %w(baz.rb)
|
106
|
+
@module.push(ary.join("/"))
|
107
|
+
end
|
108
|
+
|
109
|
+
it "pushes a file with collection" do
|
110
|
+
m = mock
|
111
|
+
m.expects(:upload_from_file).with("foo/bar/test.rb", "test.rb").returns(:remote_file)
|
112
|
+
@module.expects(:session).returns(m)
|
113
|
+
|
114
|
+
ary = %w(foo bar test.rb)
|
115
|
+
col = mock
|
116
|
+
col.expects(:add).with(:remote_file)
|
117
|
+
@module.expects(:traverse).with(["baz"], :force).returns(col)
|
118
|
+
@module.expects(:root).returns(mock(:remove))
|
119
|
+
@module.push(ary.join("/"), "baz/test.rb", :force).must_equal :remote_file
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "downloading files" do
|
125
|
+
it "pulls file to local file" do
|
126
|
+
m = mock
|
127
|
+
m.expects(:download_to_file).with("/tmp/out")
|
128
|
+
@module.expects(:get_file).with(%w(foo bar baz.txt)).returns(m)
|
129
|
+
@module.pull("foo/bar/baz.txt", "/tmp/out")
|
130
|
+
end
|
131
|
+
|
132
|
+
it "pulls file to local defaults" do
|
133
|
+
m = mock
|
134
|
+
m.expects(:download_to_file).with("baz.txt")
|
135
|
+
@module.expects(:get_file).with(%w(foo bar baz.txt)).returns(m)
|
136
|
+
@module.pull("foo/bar/baz.txt")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "deletes files" do
|
141
|
+
path = "/path/to/txt"
|
142
|
+
|
143
|
+
m = mock
|
144
|
+
m.expects(:delete).with(:blah).returns(:blah)
|
145
|
+
|
146
|
+
@module.expects(:get_file).with(%w[path to txt]).returns(m)
|
147
|
+
|
148
|
+
@module.del(path, :blah).must_equal(:blah)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "moves files" do
|
152
|
+
old_path = "/old/path/to/txt"
|
153
|
+
new_path = "/new/path/txt"
|
154
|
+
|
155
|
+
m = mock
|
156
|
+
n = mock
|
157
|
+
n.expects(:add).with(m).returns(:success)
|
158
|
+
|
159
|
+
@module.expects(:traverse).with(%w[old path to]).returns(mock(remove: true))
|
160
|
+
@module.expects(:get_file).with(%w[old path to txt]).returns(m)
|
161
|
+
@module.expects(:traverse).with(%w[new path txt], true).returns(n)
|
162
|
+
|
163
|
+
@module.mv(old_path, new_path).must_be :nil?
|
164
|
+
end
|
165
|
+
end
|
data/test/socket_test.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative "test_helper.rb"
|
2
|
+
|
3
|
+
describe GoogleDriveCompanion::Socket do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@instance = GoogleDriveCompanion::Socket.instance
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns socket filename" do
|
10
|
+
ENV.delete("gdc_socket")
|
11
|
+
|
12
|
+
@instance.socket_file.must_equal "/tmp/gdc_socket.sock"
|
13
|
+
|
14
|
+
ENV["gdc_socket"] = "/foo"
|
15
|
+
@instance.socket_file.must_equal "/foo"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns a socket" do
|
19
|
+
UNIXSocket.expects(:open).returns(:success!)
|
20
|
+
@instance.socket.must_equal(:success!)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "closes its socket" do
|
24
|
+
@instance.expects(:socket).returns(mock(close: :success!))
|
25
|
+
@instance.close.must_equal(:success!)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sends a json string to the socket" do
|
29
|
+
ary = %w[push /path/to/file]
|
30
|
+
|
31
|
+
socket = mock
|
32
|
+
socket.expects(:send).with(ary.to_json, 0)
|
33
|
+
socket.expects(:recv).with(10_000).returns(:success!)
|
34
|
+
|
35
|
+
@instance.expects(:socket).times(2).returns(socket)
|
36
|
+
@instance.digest(ary).must_equal(:success!)
|
37
|
+
end
|
38
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: google_drive_companion
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Edward Price
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: google_drive
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
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: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mocha
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
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: '0'
|
62
|
+
description: Interface with google drive from tha matrix
|
63
|
+
email:
|
64
|
+
- ted.price+github@gmail.com
|
65
|
+
executables:
|
66
|
+
- gddel
|
67
|
+
- gdmv
|
68
|
+
- gdpull
|
69
|
+
- gdpush
|
70
|
+
- gdrive
|
71
|
+
extensions: []
|
72
|
+
extra_rdoc_files: []
|
73
|
+
files:
|
74
|
+
- .gitignore
|
75
|
+
- Gemfile
|
76
|
+
- LICENSE.txt
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- bin/gddel
|
80
|
+
- bin/gdmv
|
81
|
+
- bin/gdpull
|
82
|
+
- bin/gdpush
|
83
|
+
- bin/gdrive
|
84
|
+
- google_drive_companion.gemspec
|
85
|
+
- lib/google_drive_companion.rb
|
86
|
+
- lib/google_drive_companion/server.rb
|
87
|
+
- lib/google_drive_companion/session.rb
|
88
|
+
- lib/google_drive_companion/socket.rb
|
89
|
+
- lib/google_drive_companion/version.rb
|
90
|
+
- lib/help.txt
|
91
|
+
- test/server_test.rb
|
92
|
+
- test/session_test.rb
|
93
|
+
- test/socket_test.rb
|
94
|
+
- test/test_helper.rb
|
95
|
+
homepage: https://github.com/pricees/google-drive-companion
|
96
|
+
licenses: []
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.24
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Interface with google drive from tha matrix
|
119
|
+
test_files:
|
120
|
+
- test/server_test.rb
|
121
|
+
- test/session_test.rb
|
122
|
+
- test/socket_test.rb
|
123
|
+
- test/test_helper.rb
|