syncbox 0.0.1
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/bin/syncbox +28 -0
- data/lib/syncbox.rb +50 -0
- data/lib/syncbox/store.rb +42 -0
- data/lib/syncbox/store/s3_bucket.rb +76 -0
- data/lib/syncbox/syncer.rb +40 -0
- metadata +52 -0
data/bin/syncbox
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
|
|
3
|
+
require Dir.getwd + '/lib/syncs3.rb'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
if ARGV.first == "init"
|
|
7
|
+
Syncs3.config_init
|
|
8
|
+
elsif ARGV.first == "start"
|
|
9
|
+
puts "start."
|
|
10
|
+
Syncs3.start
|
|
11
|
+
elsif ARGV.first == "stop"
|
|
12
|
+
Syncs3.stop
|
|
13
|
+
puts "Stopped."
|
|
14
|
+
elsif ARGV.first == "status"
|
|
15
|
+
Syncs3.status
|
|
16
|
+
elsif ARGV.first == "restart"
|
|
17
|
+
# Syncs3.stop
|
|
18
|
+
# puts "restart."
|
|
19
|
+
# Syncs3.start
|
|
20
|
+
elsif ARGV.first == "list" && ARGV.last == "files"
|
|
21
|
+
Syncs3.file_status
|
|
22
|
+
elsif ARGV.first == "help"
|
|
23
|
+
puts "help"
|
|
24
|
+
else
|
|
25
|
+
put "No such command."
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
data/lib/syncbox.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require_relative 'syncbox/store.rb'
|
|
4
|
+
require_relative 'syncbox/syncer.rb'
|
|
5
|
+
|
|
6
|
+
module Syncbox
|
|
7
|
+
class Syncbox
|
|
8
|
+
|
|
9
|
+
def self.start
|
|
10
|
+
syncbox = Syncbox.new
|
|
11
|
+
syncbox.start
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# read config path from command line, and load configs
|
|
15
|
+
# init a store instance
|
|
16
|
+
#
|
|
17
|
+
def initialize
|
|
18
|
+
config_file = option_parser[:config]
|
|
19
|
+
@local_directory = read_config(config_file, "local_directory")
|
|
20
|
+
config_s3 = read_config(config_file, "s3")
|
|
21
|
+
@store = Store.new("S3", config_s3)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Init a Syncer object, and start sync
|
|
25
|
+
#
|
|
26
|
+
def start
|
|
27
|
+
syncer = Syncer.new(@local_directory, @store)
|
|
28
|
+
syncer.sync
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
def read_config(config_file, store)
|
|
33
|
+
config = YAML.load_file(config_file)
|
|
34
|
+
config[store]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def option_parser
|
|
38
|
+
options = {}
|
|
39
|
+
option_parser = OptionParser.new do |opts|
|
|
40
|
+
opts.banner = 'This is help messages.'
|
|
41
|
+
opts.on('-c file', '--config file', 'Pass-in config file path') do |value|
|
|
42
|
+
options[:config] = value
|
|
43
|
+
end
|
|
44
|
+
end.parse!
|
|
45
|
+
options
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require_relative 'store/s3_bucket.rb'
|
|
2
|
+
|
|
3
|
+
module Syncbox
|
|
4
|
+
class Store
|
|
5
|
+
|
|
6
|
+
STORES = { "S3" => "S3Bucket", "Glacier" => "Glacier"}
|
|
7
|
+
|
|
8
|
+
# Initializes a store object
|
|
9
|
+
#
|
|
10
|
+
# @param [String] store
|
|
11
|
+
# @param [HASH] options
|
|
12
|
+
#
|
|
13
|
+
def initialize(store, options={})
|
|
14
|
+
raise ArgumentError, "Store #{store} is not supported." unless STORES.fetch(store)
|
|
15
|
+
class_name = STORES[store]
|
|
16
|
+
@store = Object.const_get("Syncbox").const_get(class_name).new(options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Add file to store.
|
|
20
|
+
#
|
|
21
|
+
# @param [String] file_path
|
|
22
|
+
#
|
|
23
|
+
# @return a public (not authenticated) URL for the object
|
|
24
|
+
#
|
|
25
|
+
def add(file_path)
|
|
26
|
+
@store.upload(file_path)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# delete file from store.
|
|
30
|
+
#
|
|
31
|
+
# @param [String] file_path
|
|
32
|
+
#
|
|
33
|
+
# @return nil
|
|
34
|
+
#
|
|
35
|
+
def delete(file_path)
|
|
36
|
+
@store.delete(file_path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
alias modify add
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'aws-sdk'
|
|
2
|
+
|
|
3
|
+
module Syncbox
|
|
4
|
+
class S3Bucket
|
|
5
|
+
|
|
6
|
+
# Initializes a s3 bucket object
|
|
7
|
+
#
|
|
8
|
+
# @param [Hash] options
|
|
9
|
+
#
|
|
10
|
+
# options must include:
|
|
11
|
+
# * access_key_id
|
|
12
|
+
# * secret_access_key
|
|
13
|
+
# * bucket_name
|
|
14
|
+
#
|
|
15
|
+
# Note: modify the local files, triggers upload.
|
|
16
|
+
# Upload same name file, S3 will automatically replaced the original file.
|
|
17
|
+
# Cancel the upload in the middle of uploading will not replace the originmal file.
|
|
18
|
+
#
|
|
19
|
+
def initialize(options)
|
|
20
|
+
arguement_check(options)
|
|
21
|
+
s3 = AWS::S3.new(:access_key_id => options["access_key_id"], :secret_access_key => options["secret_access_key"])
|
|
22
|
+
bucket_name = options["bucket_name"]
|
|
23
|
+
@bucket = s3.buckets[bucket_name]
|
|
24
|
+
remote_accessible_check
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Uploads file to bucket.
|
|
28
|
+
#
|
|
29
|
+
# @param [String] file_path
|
|
30
|
+
#
|
|
31
|
+
# @return a public (not authenticated) URL for the object
|
|
32
|
+
#
|
|
33
|
+
def upload(file_path)
|
|
34
|
+
file_name = File.basename(file_path)
|
|
35
|
+
object = @bucket.objects[file_name]
|
|
36
|
+
object.write(:file => file_path)
|
|
37
|
+
object.public_url
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# delete file from bucket.
|
|
41
|
+
#
|
|
42
|
+
# @param [String] file_path
|
|
43
|
+
#
|
|
44
|
+
# @return nil
|
|
45
|
+
#
|
|
46
|
+
def delete(file_path)
|
|
47
|
+
file_name = File.basename(file_path)
|
|
48
|
+
object = @bucket.objects[file_name]
|
|
49
|
+
object.delete
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
# check s3 arguments contains access_key_id, secret_access_key and bucket_name
|
|
55
|
+
#
|
|
56
|
+
def arguement_check(options)
|
|
57
|
+
begin
|
|
58
|
+
options.fetch("access_key_id") && options.fetch("secret_access_key") && options.fetch("bucket_name")
|
|
59
|
+
rescue KeyError
|
|
60
|
+
raise ArgumentError, "The access_key_id, secret_access_key and bucket_name options are required."
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# check S3 access_key_id, secret_access_key is valid and bucket_name exists.
|
|
65
|
+
#
|
|
66
|
+
def remote_accessible_check
|
|
67
|
+
begin
|
|
68
|
+
raise ArgumentError, "Bucket doesn't exsit." unless @bucket.exists?
|
|
69
|
+
rescue AWS::S3::Errors::InvalidAccessKeyId
|
|
70
|
+
raise ArgumentError, 'AWS Access Key Id does not exist in our records.'
|
|
71
|
+
rescue AWS::S3::Errors::SignatureDoesNotMatch
|
|
72
|
+
raise ArgumentError, 'The request signature we calculated does not match the signature you provided. Check your key and signing method.'
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative 'store.rb'
|
|
2
|
+
require 'listen'
|
|
3
|
+
|
|
4
|
+
module Syncbox
|
|
5
|
+
class Syncer
|
|
6
|
+
|
|
7
|
+
# Initializes listener
|
|
8
|
+
#
|
|
9
|
+
# @param [String] local_directory
|
|
10
|
+
# @param [Store] store
|
|
11
|
+
#
|
|
12
|
+
# Note: raise arguement error when local_directory does not exist.
|
|
13
|
+
def initialize(local_directory, store)
|
|
14
|
+
raise ArgumentError, "Local directory #{local_directory} does not exist." unless Dir.exists?(local_directory)
|
|
15
|
+
@local_directory = local_directory
|
|
16
|
+
@store = store
|
|
17
|
+
@listener = Listen.to(local_directory).latency(0.1)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Listen to local directory
|
|
21
|
+
# Sync local action to remote store
|
|
22
|
+
#
|
|
23
|
+
def sync
|
|
24
|
+
callback = Proc.new do |modified, added, removed|
|
|
25
|
+
added && added.each do |add_file|
|
|
26
|
+
@store.add(@local_directory + "/" + add_file)
|
|
27
|
+
end
|
|
28
|
+
modified && modified.each do |modified_file|
|
|
29
|
+
@store.modify(@local_directory + "/" + modified_file)
|
|
30
|
+
end
|
|
31
|
+
removed && removed.each do |remove_file|
|
|
32
|
+
@store.remove(@local_directory + "/" + remove_file)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
@listener.change(&callback) # convert the callback to a block and register it
|
|
36
|
+
@listener.start! # have to use !
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: syncbox
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Ian Dai
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2014-02-01 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: A gem that automatically sync file to S3 or Glacier.
|
|
15
|
+
email: iandai.jp@gmail.com
|
|
16
|
+
executables:
|
|
17
|
+
- syncbox
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- lib/syncbox.rb
|
|
22
|
+
- lib/syncbox/store.rb
|
|
23
|
+
- lib/syncbox/store/s3_bucket.rb
|
|
24
|
+
- lib/syncbox/syncer.rb
|
|
25
|
+
- bin/syncbox
|
|
26
|
+
homepage: http://iandai.cc
|
|
27
|
+
licenses:
|
|
28
|
+
- MIT
|
|
29
|
+
post_install_message:
|
|
30
|
+
rdoc_options: []
|
|
31
|
+
require_paths:
|
|
32
|
+
- lib
|
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
34
|
+
none: false
|
|
35
|
+
requirements:
|
|
36
|
+
- - ! '>='
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0'
|
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
|
+
none: false
|
|
41
|
+
requirements:
|
|
42
|
+
- - ! '>='
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: '0'
|
|
45
|
+
requirements: []
|
|
46
|
+
rubyforge_project:
|
|
47
|
+
rubygems_version: 1.8.23
|
|
48
|
+
signing_key:
|
|
49
|
+
specification_version: 3
|
|
50
|
+
summary: syncbox
|
|
51
|
+
test_files: []
|
|
52
|
+
has_rdoc:
|