google_drive_companion 0.1.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.
- 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
|