savedatasync 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 98c3e79e601a2a344ead97ff13b53ab16a92d8c7
4
+ data.tar.gz: 6a2a5920b1a018fd665eb674b31480c0327e4372
5
+ SHA512:
6
+ metadata.gz: 877b51981b54009945fabc99f547148b27da6a3c89ba7724fd9aa9628f3c3ab68df2d5366d125f8250fa80bb9f04cc7847c57edcb59ee6ea5fbb8fc3087db1ee
7
+ data.tar.gz: f1060c69611d05449947983d71393c86c31399771d25065fd92775b6d2ab1ce1db377893ea580754aba179acebb3d59d8a83c0b689be332bcf2fad26152be09b
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in savedatasync.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Kensuke Sawada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ # Savedata Synchronizer
2
+
3
+ ## Install
4
+ ```
5
+ $ git clone https://github.com/sawaken/savedatasync.git
6
+ $ cd savedatasync
7
+ $ rake install
8
+ ```
9
+
10
+ ## Usage
11
+ ```
12
+ $ sdsync -h
13
+ ```
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+
4
+ require 'savedatasync'
5
+ Version = Savedatasync::VERSION
6
+ Savedatasync::Command.from_argv(ARGV).run
@@ -0,0 +1,189 @@
1
+ require "savedatasync/version"
2
+ require "savedatasync/usage"
3
+ require "fileutils"
4
+ require 'optparse'
5
+
6
+ module Savedatasync
7
+ class Command < Struct.new(
8
+ :sub_command, :force, :title, :remote_dir_path, :local_path, :remote_filename
9
+ )
10
+
11
+ def self.from_argv(argv)
12
+ com = extract_argv(new, argv.map { |a| a.encode("UTF-8") })
13
+ com.title ||= File.basename(File.dirname(com.local_path)).encode("UTF-8")
14
+ com.remote_dir_path ||= File.expand_path(default_remote_dir).encode("UTF-8")
15
+ com.remote_filename ||= File.basename(com.local_path).encode("UTF-8")
16
+ return com
17
+ end
18
+
19
+ def self.default_remote_dir
20
+ ENV['sdsync_remote_dir'].encode("UTF-8") || "#{Dir.home.encode("UTF-8")}/Dropbox/sdsync"
21
+ end
22
+
23
+ def self.extract_argv(com, argv)
24
+ opt_parser = OptionParser.new do |parse|
25
+ parse.banner = Savedatasync::USAGE
26
+ desc = 'force to execute even if there is an existing file'
27
+ parse.on('--force', desc) do
28
+ com.force = true
29
+ end
30
+ desc = 'specify title. basename(dirname(savedata-file)) is used as default'
31
+ parse.on('--title=TITLE', desc) do |title|
32
+ com.title = title
33
+ end
34
+ desc = "specify the path of remote directory. ENV['sdsync_remote_dir'] or ~/Dropbox/sdsync is used as default"
35
+ parse.on('--remote=PATH', desc) do |path|
36
+ com.remote_dir_path = path
37
+ end
38
+ end
39
+ sub_command, *args_without_option = opt_parser.parse(argv)
40
+ unless ['get', 'put', 'cut', 'status'].include?(sub_command)
41
+ STDERR.puts "invalid subcommand #{sub_command}"
42
+ STDERR.puts opt_parser.help
43
+ exit(1)
44
+ end
45
+ unless 1 <= args_without_option.size && args_without_option.size <= 2
46
+ STDERR.puts "invalid length of arguments"
47
+ STDERR.puts opt_parser.help
48
+ print_usage
49
+ exit(1)
50
+ end
51
+ com.sub_command = sub_command
52
+ com.local_path = File.expand_path(args_without_option[0]).encode("UTF-8")
53
+ com.remote_filename = args_without_option[1]
54
+ return com
55
+ rescue OptionParser::InvalidOption => error
56
+ STDERR.puts error.message
57
+ STDERR.puts opt_parser.help
58
+ exit(1)
59
+ end
60
+
61
+ def run
62
+ op = Operator.new(title, force, remote_dir_path)
63
+ case sub_command
64
+ when 'put'
65
+ op.put(local_path, remote_filename)
66
+ when 'get'
67
+ op.get(local_path, remote_filename)
68
+ when 'cut'
69
+ op.cut(local_path, remote_filename)
70
+ when 'status'
71
+ op.status(local_path, remote_filename)
72
+ end
73
+ rescue Operator::Error => err
74
+ STDERR.puts err.message
75
+ exit(1)
76
+ end
77
+ end
78
+
79
+ class Operator
80
+ Error = Class.new(StandardError)
81
+
82
+ def initialize(title, force, remote_dir_path)
83
+ @title = title
84
+ @force = force
85
+ @remote_dir_path = remote_dir_path
86
+ title_dir_path = File.expand_path(@remote_dir_path + '/' + @title).encode("UTF-8")
87
+ unless File.directory?(@remote_dir_path)
88
+ raise Error.new("remote dir #{@remote_dir_path} does not exist")
89
+ end
90
+ FileUtils.mkdir(title_dir_path) unless File.exist?(title_dir_path)
91
+ end
92
+
93
+ def put(local_path, remote_filename)
94
+ remote_path = construct_remote_path(local_path, remote_filename)
95
+ lstatus = local_status(local_path, remote_path)
96
+ rstatus = remote_status(remote_path)
97
+ case lstatus
98
+ when :entity
99
+ if rstatus == :entity
100
+ unless @force
101
+ raise Error.new("remote file '#{remote_path}' exists. use -f to force")
102
+ end
103
+ FileUtils.rm_r(remote_path)
104
+ end
105
+ FileUtils.mv(local_path, remote_path)
106
+ FileUtils.symlink(remote_path, local_path)
107
+ return true
108
+ when :empty
109
+ raise Error.new("local file '#{local_path}' is empty")
110
+ when :valid_link
111
+ if rstatus == :empty
112
+ raise Error.new("local file '#{local_path}' is broken link. remove it to continue")
113
+ end
114
+ return true
115
+ when :invalid_link
116
+ raise Error.new("local file '#{local_path} is broken link. remove it to continue")
117
+ end
118
+ end
119
+
120
+ def get(local_path, remote_filename)
121
+ remote_path = construct_remote_path(local_path, remote_filename)
122
+ lstatus = local_status(local_path, remote_path)
123
+ rstatus = remote_status(remote_path)
124
+ case lstatus
125
+ when :entity
126
+ if rstatus == :empty
127
+ raise Error.new("remote file '#{remote_path}' is empty")
128
+ end
129
+ unless @force
130
+ raise Error.new("local file '#{local_path}' exists. use -f to force")
131
+ end
132
+ FileUtils.rm_rf(local_path)
133
+ FileUtils.symlink(remote_path, local_path)
134
+ return true
135
+ when :empty
136
+ if rstatus == :empty
137
+ raise Error.new("remote file '#{remote_path}' is empty")
138
+ end
139
+ FileUtils.symlink(remote_path, local_path)
140
+ return true
141
+ when :valid_link
142
+ if rstatus == :empty
143
+ raise Error.new("local file '#{local_path}' is broken link. remove it to continue")
144
+ end
145
+ return true
146
+ when :invalid_link
147
+ raise Error.new("local file '#{local_path} is broken link. remove it to continue")
148
+ end
149
+ end
150
+
151
+ def cut(local_path, remote_filename)
152
+ remote_path = construct_remote_path(local_path, remote_filename)
153
+ lstatus = local_status(local_path, remote_path)
154
+ rstatus = remote_status(remote_path)
155
+ unless lstatus == :valid_link && rstatus == :entity
156
+ raise Error.new("cannot cut un-synced file")
157
+ end
158
+ FileUtils.rm_r(local_path)
159
+ FileUtils.cp_r(remote_path, local_path)
160
+ return true
161
+ end
162
+
163
+ def status(local_path, remote_filename)
164
+ remote_path = construct_remote_path(local_path, remote_filename)
165
+ lstatus = local_status(local_path, remote_path)
166
+ rstatus = remote_status(remote_path)
167
+ STDERR.puts "[#{@title}] #{local_path} (#{lstatus}) <==> #{remote_path} (#{rstatus})"
168
+ return true
169
+ end
170
+
171
+ private
172
+
173
+ def local_status(local_path, remote_path)
174
+ if File.symlink?(local_path)
175
+ File.readlink(local_path).encode("UTF-8") == remote_path ? :valid_link : :invalid_link
176
+ else
177
+ File.exist?(local_path) ? :entity : :empty
178
+ end
179
+ end
180
+
181
+ def remote_status(remote_path)
182
+ File.exist?(remote_path) ? :entity : :empty
183
+ end
184
+
185
+ def construct_remote_path(local_path, remote_filename)
186
+ File.expand_path("#{@remote_dir_path}/#{@title}/#{remote_filename}").encode("UTF-8")
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,42 @@
1
+ module Savedatasync
2
+ USAGE = <<-TXT
3
+ usage: sdsync <subcommand> [options] savedata-file [savedata-name]
4
+
5
+ <subcommand>:
6
+ get make a link using the entity in remote
7
+ put make a link using the entity in local
8
+ cut delete the link and copy the entity from remote
9
+ status print status of local and remote
10
+
11
+ <get> behavior:
12
+ | local/remote | entity | empty |
13
+ |---------------|-------------|-----------|
14
+ | invalid_link | error | error |
15
+ | valid_link | do nothing | error |
16
+ | entity | do if -f | error |
17
+ | empty | do | error |
18
+
19
+ <put> behavior:
20
+ | local/remote | entity | empty |
21
+ |---------------|-------------|-----------|
22
+ | invalid_link | error | error |
23
+ | valid_link | do nothing | error |
24
+ | entity | do if -f | do |
25
+ | empty | error | error |
26
+
27
+ <cut> behavior:
28
+ | local/remote | entity | empty |
29
+ |---------------|-------------|-----------|
30
+ | invalid_link | error | error |
31
+ | valid_link | do | error |
32
+ | entity | error | error |
33
+ | empty | error | error |
34
+
35
+ arguments:
36
+ savedata-file path of savedata file or directory to target
37
+ savedata-name name of savedata file or directory to store in remote
38
+ savedata-file's basename is used as default
39
+
40
+ [options]:
41
+ TXT
42
+ end
@@ -0,0 +1,3 @@
1
+ module Savedatasync
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'savedatasync/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "savedatasync"
8
+ spec.version = Savedatasync::VERSION
9
+ spec.authors = ["Kensuke Sawada"]
10
+ spec.email = ["sasasawada@gmail.com"]
11
+
12
+ spec.summary = %q{Savedata Synchronizer}
13
+ spec.homepage = "https://github.com/sawaken/savedatasync"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.13"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: savedatasync
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Kensuke Sawada
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-03-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description:
56
+ email:
57
+ - sasasawada@gmail.com
58
+ executables:
59
+ - sdsync
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - exe/sdsync
69
+ - lib/savedatasync.rb
70
+ - lib/savedatasync/usage.rb
71
+ - lib/savedatasync/version.rb
72
+ - savedatasync.gemspec
73
+ homepage: https://github.com/sawaken/savedatasync
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.6.10
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Savedata Synchronizer
97
+ test_files: []