homesync 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +11 -0
- data/LICENSE +20 -0
- data/README.md +114 -0
- data/bin/homesync +17 -0
- data/lib/homesync.rb +5 -0
- data/lib/homesync/cli.rb +164 -0
- data/lib/homesync/version.rb +3 -0
- data/spec/fixtures/home/Code/hello_world.rb +1 -0
- data/spec/fixtures/home/Code/hello_world/app.rb +5 -0
- data/spec/fixtures/home/Code/hello_world/config.ru +2 -0
- data/spec/fixtures/home/DB/HS/custom_homesync +1 -0
- data/spec/fixtures/home/Dropbox/HomeSync/bin/colors +7 -0
- data/spec/fixtures/home/Dropbox/HomeSync/synced +1 -0
- data/spec/fixtures/home/Dropbox/HomeSync/tasks/home +0 -0
- data/spec/fixtures/home/Dropbox/HomeSync/tasks/work +0 -0
- data/spec/fixtures/home/Dropbox/HomeSync/tasks/world_hunger +0 -0
- data/spec/fixtures/home/Dropbox/HomeSync/todo.txt +1 -0
- data/spec/fixtures/home/bin/colors +8 -0
- data/spec/fixtures/home/existing_link +0 -0
- data/spec/fixtures/home/synced +1 -0
- data/spec/fixtures/home/tasks/garden +0 -0
- data/spec/fixtures/home/tasks/home +0 -0
- data/spec/fixtures/home/tasks/shopping +0 -0
- data/spec/fixtures/home/to/infinity/and/beyond +0 -0
- data/spec/homesync/cli_spec.rb +301 -0
- data/spec/spec_helper.rb +37 -0
- metadata +128 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 László Bácsi
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
**Note**: This project is being developed following the
|
2
|
+
[RDD](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html)
|
3
|
+
model. Not all of the functionality described in this README have been
|
4
|
+
implemented yet. Missing pieces are noted in the Usage section.
|
5
|
+
|
6
|
+
# HomeSync
|
7
|
+
|
8
|
+
HomeSync makes it easy to synchronize any file under your home directory
|
9
|
+
with other machines through Dropbox by providing a simple commandline
|
10
|
+
interface for adding and removing symlinks to your files. Furthermore,
|
11
|
+
it knows how to handle several key directories correctly and how to
|
12
|
+
synchronize application preferences and data.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
$ gem install homesync
|
17
|
+
$ homesync setup [-p ~/path/to/dropbox/homesync]
|
18
|
+
|
19
|
+
## How it works
|
20
|
+
|
21
|
+
HomeSync uses a directory in your dropbox (`~/Dropbox/HomeSync` by
|
22
|
+
default) to store the files and directories you want to be synchronized.
|
23
|
+
When you add a file to HomeSync it moves the original file to this
|
24
|
+
location and creates a symbolic link in its original place.
|
25
|
+
|
26
|
+
Files are always stored in HomeSync with the same relative path they had
|
27
|
+
in your home directory. For example, if you have a file in
|
28
|
+
`~/Code/script.rb` and you have it synchronized by HomeSync, it will be
|
29
|
+
moved to `~/Dropbox/HomeSync/Code/script.rb` and `~/Code/script.rb` will
|
30
|
+
be a symbolic link pointing to this new path.
|
31
|
+
|
32
|
+
HomeSync handles `plist` files in `~/Library/Preferences/` a bit
|
33
|
+
differently. These files are usually overwritten when their application
|
34
|
+
exits. HomeSync uses a launch agent to watch this directory and the
|
35
|
+
corresponding HomeSync directory for changes. When a file that needs to
|
36
|
+
be synced is changed in either of these directories, it would be copied
|
37
|
+
to the other folder.
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
$ homesync setup [-p ~/path/to/dropbox/homesync]
|
42
|
+
|
43
|
+
Creates a launch agent to monitor changes of preferences files. The `-p`
|
44
|
+
option tells HomeSync where to put synchronized files (defaults to
|
45
|
+
`~/Dropbox/HomeSync`). This will be stored in the `~/.homesync`
|
46
|
+
configuration file, but all the other commands accept this option too.
|
47
|
+
|
48
|
+
$ homesync sync [--overwrite-local | --overwrite-homesync] ~/path/to/file_or_dir
|
49
|
+
|
50
|
+
Tells HomeSync to synchronize the file. This is the default command
|
51
|
+
meaning that the command name may be omitted. The outcome of this
|
52
|
+
command depends on whether the file exists and whether there's a file
|
53
|
+
with the same relative path in HomeSync:
|
54
|
+
|
55
|
+
* original file doesn't exist
|
56
|
+
* and homesync file doesn't exist: error
|
57
|
+
* and homesync file exists: create symbolic link to the homesync file
|
58
|
+
* original file is a symbolic link
|
59
|
+
* to its homesync file: nothing to do
|
60
|
+
* to somewhere else: error (homesync doesn't handle this case)
|
61
|
+
* original file is a regular file or directory
|
62
|
+
* and homesync file doesn't exist: move it to homesync and create
|
63
|
+
symbolic link to it
|
64
|
+
* and homesync file exists: asks whether to overwrite the current
|
65
|
+
local file and sync what's in HomeSync (`--overwrite-local` to do
|
66
|
+
this without aking) or update HomeSync with the current local file
|
67
|
+
in and sync that instead (`--overwrite-homesync` to do this without
|
68
|
+
asking)
|
69
|
+
|
70
|
+
$ homesync unsync [-r] ~/path/to/file_or_directory
|
71
|
+
|
72
|
+
*Not implemented yet.* Given that the argument is a symbolic link to an
|
73
|
+
existing file or directory in HomeSync, copies this file to its original
|
74
|
+
place overwriting the symbolic link. Use the `-r` option to remove the
|
75
|
+
file from homesync afterwards.
|
76
|
+
|
77
|
+
### Custom behaviors (*none of it implemented yet*)
|
78
|
+
|
79
|
+
$ homesync sync:pref Application
|
80
|
+
$ homesync unsync:pref Application
|
81
|
+
|
82
|
+
Syncs or unsyncs the preferences file of the application (e.g. for
|
83
|
+
TextMate this would be
|
84
|
+
`~/Library/Preferences/com.macromates.textmate.plist`). Symlinking
|
85
|
+
wouldn't work because of overwrites, so this works by using a launch
|
86
|
+
agent to monitor changes to this file.
|
87
|
+
|
88
|
+
$ homesync sync:appsupport Application
|
89
|
+
$ homesync unsync:appsupport Application
|
90
|
+
|
91
|
+
Syncs or unsyncs the application support directory of the application
|
92
|
+
(e.g. for TextMate this would be `~/Library/Application
|
93
|
+
Support/TextMate`).
|
94
|
+
|
95
|
+
$ homesync sync:app Application
|
96
|
+
$ homesync unsync:app Application
|
97
|
+
|
98
|
+
Syncs or unsyncs both the application's preferences file and its
|
99
|
+
application support directory.
|
100
|
+
|
101
|
+
## Note on Patches/Pull Requests
|
102
|
+
|
103
|
+
* Fork the project.
|
104
|
+
* Make your feature addition or bug fix.
|
105
|
+
* Add tests for it. This is important so I don't break it in a future
|
106
|
+
version unintentionally.
|
107
|
+
* Commit, do not mess with rakefile, version, or history. (if you want
|
108
|
+
to have your own version, that is fine but bump version in a commit by
|
109
|
+
itself I can ignore when I pull)
|
110
|
+
* Send me a pull request. Bonus points for topic branches.
|
111
|
+
|
112
|
+
## Copyright
|
113
|
+
|
114
|
+
Copyright (c) 2011 László Bácsi. See LICENSE for details.
|
data/bin/homesync
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'homesync/version'
|
5
|
+
rescue LoadError
|
6
|
+
$:.unshift(File.expand_path("../lib", File.dirname(__FILE__)))
|
7
|
+
require 'homesync/version'
|
8
|
+
end
|
9
|
+
|
10
|
+
if %w(--version -v).include? ARGV.first
|
11
|
+
puts "HomeSync #{HomeSync::VERSION}"
|
12
|
+
exit(0)
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'homesync/cli'
|
16
|
+
|
17
|
+
HomeSync::CLI.start
|
data/lib/homesync.rb
ADDED
data/lib/homesync/cli.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'pathname'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module HomeSync
|
6
|
+
class CLI < Thor
|
7
|
+
|
8
|
+
class_option :homesync_path, :aliases => "-p"
|
9
|
+
|
10
|
+
default_task :sync
|
11
|
+
|
12
|
+
desc "setup", "Create the launch agent and configure HomeSync"
|
13
|
+
long_desc <<-EOH
|
14
|
+
Create a launch agent to monitor changes of preferences files
|
15
|
+
Store homesync path configuration (-p option) in ~/.homesync
|
16
|
+
EOH
|
17
|
+
def setup
|
18
|
+
update_homesync_config if options[:homesync_path]
|
19
|
+
update_launch_agent
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "sync FILE", "Setup syncing for file or directory"
|
23
|
+
long_desc <<-EOH
|
24
|
+
Move the file to the HomeSync directory and create a symbolic link in its
|
25
|
+
place. Or setup a symbolic link in this location if the file doesn't exist
|
26
|
+
but a matching file in HomeSync does.
|
27
|
+
|
28
|
+
When both files exist you will be asked what to do. Alternatively you could
|
29
|
+
tell HomeSync which road to go using one of the options of this command.
|
30
|
+
EOH
|
31
|
+
method_options :overwrite_local => :boolean, :overwrite_homesync => :boolean
|
32
|
+
def sync(path)
|
33
|
+
if options[:overwrite_local] and options[:overwrite_homesync]
|
34
|
+
error "--overwrite-local and --overwrite-homesync cannot be used together"
|
35
|
+
end
|
36
|
+
|
37
|
+
path = Pathname.new(path).expand_path
|
38
|
+
relative_from_home = path.relative_path_from(home_path)
|
39
|
+
|
40
|
+
if relative_from_home.to_s =~ %r{^../}
|
41
|
+
error "#{path} is not inside your home directory"
|
42
|
+
end
|
43
|
+
|
44
|
+
sync_path = homesync_path.join(relative_from_home)
|
45
|
+
|
46
|
+
if path.symlink?
|
47
|
+
target = path.readlink
|
48
|
+
target = path.dirname.realpath.join(target) if target.relative?
|
49
|
+
if target == sync_path
|
50
|
+
error "#{path} is already syncing"
|
51
|
+
else
|
52
|
+
error "#{path} is a symlink pointing somewhere else than its place in #{homesync_path}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if path.exist?
|
57
|
+
if sync_path.exist?
|
58
|
+
if options[:overwrite_local] or (not options[:overwrite_homesync] and shell.file_collision(path))
|
59
|
+
if path.file? then path.unlink else path.rmtree end
|
60
|
+
path.make_symlink(sync_path.to_s)
|
61
|
+
say "Replaced #{path} with symlink to #{sync_path}"
|
62
|
+
elsif options[:overwrite_homesync] or shell.file_collision(sync_path)
|
63
|
+
if sync_path.file? then sync_path.unlink else sync_path.rmtree end
|
64
|
+
path.rename(sync_path)
|
65
|
+
path.make_symlink(sync_path.to_s)
|
66
|
+
say "Replaced #{sync_path} with local version and created symlink to it"
|
67
|
+
else
|
68
|
+
say "Kept both versions, syncing has been cancelled"
|
69
|
+
end
|
70
|
+
else
|
71
|
+
sync_path.dirname.mkpath
|
72
|
+
path.rename(sync_path)
|
73
|
+
path.make_symlink(sync_path.to_s)
|
74
|
+
say "Moved #{path} to HomeSync and created a symlink to it"
|
75
|
+
end
|
76
|
+
else
|
77
|
+
if sync_path.exist?
|
78
|
+
path.make_symlink(sync_path.to_s)
|
79
|
+
say "Created link to #{sync_path}"
|
80
|
+
else
|
81
|
+
error "#{path} doesn't exist"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def error(message)
|
89
|
+
raise Thor::Error, message
|
90
|
+
end
|
91
|
+
|
92
|
+
def home_path
|
93
|
+
Pathname.new(ENV['HOME'])
|
94
|
+
end
|
95
|
+
|
96
|
+
def homesync_path
|
97
|
+
Pathname.new(
|
98
|
+
options[:homesync_path] ||
|
99
|
+
homesync_config['homesync_path'] ||
|
100
|
+
"~/Dropbox/HomeSync"
|
101
|
+
).expand_path
|
102
|
+
end
|
103
|
+
|
104
|
+
def config_file
|
105
|
+
home_path.join(".homesync")
|
106
|
+
end
|
107
|
+
|
108
|
+
def homesync_config
|
109
|
+
@homesync_config ||= config_file.exist? ? YAML.load_file(config_file) : {}
|
110
|
+
end
|
111
|
+
|
112
|
+
def update_homesync_config
|
113
|
+
relative_homesync_path = "~/" + homesync_path.relative_path_from(home_path).to_s
|
114
|
+
if homesync_config['homesync_path'] != relative_homesync_path
|
115
|
+
homesync_config['homesync_path'] = relative_homesync_path
|
116
|
+
config_file.open("w") { |f| f.write(homesync_config.to_yaml) }
|
117
|
+
puts "Configuration written to #{config_file}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def update_launch_agent
|
122
|
+
launch_agent_path = home_path.join("Library/LaunchAgents/com.icanscale.homesync.plist")
|
123
|
+
executable_path = File.expand_path($0 =~ %r{/} ? $0 : %x{which #{$0}})
|
124
|
+
launch_agent = <<-EOF
|
125
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
126
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
127
|
+
<plist version="1.0">
|
128
|
+
<dict>
|
129
|
+
<key>Label</key>
|
130
|
+
<string>com.icanscale.homesync</string>
|
131
|
+
<key>OnDemand</key>
|
132
|
+
<true/>
|
133
|
+
<key>ProgramArguments</key>
|
134
|
+
<array>
|
135
|
+
<string>#{executable_path}</string>
|
136
|
+
<string>sync:pref</string>
|
137
|
+
</array>
|
138
|
+
<key>RunAtLoad</key>
|
139
|
+
<true/>
|
140
|
+
<key>WatchPaths</key>
|
141
|
+
<array>
|
142
|
+
<string>#{home_path}/Library/Preferences</string>
|
143
|
+
<string>#{homesync_path}/Library/Preferences</string>
|
144
|
+
</array>
|
145
|
+
</dict>
|
146
|
+
</plist>
|
147
|
+
EOF
|
148
|
+
|
149
|
+
if launch_agent_path.exist?
|
150
|
+
if launch_agent_path.read == launch_agent
|
151
|
+
say "Launch Agent already exists"
|
152
|
+
else
|
153
|
+
launch_agent_path.open("w") { |f| f.write(launch_agent) }
|
154
|
+
say "Updated Launch Agent"
|
155
|
+
end
|
156
|
+
else
|
157
|
+
launch_agent_path.dirname.mkpath
|
158
|
+
launch_agent_path.open("w") { |f| f.write(launch_agent) }
|
159
|
+
say "Created Launch Agent"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
puts "Hello World!"
|
@@ -0,0 +1 @@
|
|
1
|
+
abbreviated
|
@@ -0,0 +1 @@
|
|
1
|
+
I'm already synced
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
Use HomeSync to sync files between your computers
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
I'm already synced
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,301 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HomeSync::CLI do
|
4
|
+
|
5
|
+
let(:stdin) { nil }
|
6
|
+
let(:stdout) { @stdout }
|
7
|
+
let(:stderr) { @stderr }
|
8
|
+
let(:command) { "" }
|
9
|
+
let(:args) { "" }
|
10
|
+
let(:home) { Pathname.new(ENV['HOME']) }
|
11
|
+
let(:homesync_path) { home.join("Dropbox/HomeSync") }
|
12
|
+
|
13
|
+
before do
|
14
|
+
setup_fixtures
|
15
|
+
@stdout, @stderr = capture_io(stdin) { homesync "#{command} #{args}" }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#setup' do
|
19
|
+
|
20
|
+
let(:command) { "setup" }
|
21
|
+
|
22
|
+
let(:launch_agent_path) { Pathname.new(File.expand_path("~/Library/LaunchAgents/com.icanscale.homesync.plist")) }
|
23
|
+
let(:launch_agent) { launch_agent_path.read }
|
24
|
+
|
25
|
+
shared_examples_for "a launch agent" do
|
26
|
+
specify { launch_agent_path.should exist }
|
27
|
+
specify { launch_agent.should =~ %r{<string>#{ENV['HOME']}/Library/Preferences</string>} }
|
28
|
+
specify { launch_agent.should =~ %r{<string>#{homesync_path}/Library/Preferences</string>} }
|
29
|
+
end
|
30
|
+
|
31
|
+
shared_examples_for "a launch agent generator" do
|
32
|
+
context "when launch agent doesn't exist" do
|
33
|
+
specify { stdout.should include("Created Launch Agent") }
|
34
|
+
it_should_behave_like "a launch agent"
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when launch agent exists with same content" do
|
38
|
+
before do
|
39
|
+
@stdout, @stderr = capture_io { homesync "#{command} #{args}" }
|
40
|
+
end
|
41
|
+
|
42
|
+
specify { stdout.should include("Launch Agent already exists") }
|
43
|
+
it_should_behave_like "a launch agent"
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when launch agent exists with different content" do
|
47
|
+
before do
|
48
|
+
launch_agent_path.dirname.mkpath
|
49
|
+
File.open(launch_agent_path, "w") { |f| f.write("x") }
|
50
|
+
@stdout, @stderr = capture_io { homesync "#{command} #{args}" }
|
51
|
+
end
|
52
|
+
|
53
|
+
specify { stdout.should include("Updated Launch Agent") }
|
54
|
+
it_should_behave_like "a launch agent"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "without options" do
|
59
|
+
it_should_behave_like "a launch agent generator"
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with -p option" do
|
63
|
+
let(:args) { "-p ~/DB/HS" }
|
64
|
+
let(:homesync_path) { home.join("DB/HS") }
|
65
|
+
|
66
|
+
it_should_behave_like "a launch agent generator"
|
67
|
+
|
68
|
+
context "creates configuration file to store homesync path" do
|
69
|
+
specify { stdout.should include("Configuration written to") }
|
70
|
+
specify { YAML.load_file(File.expand_path("~/.homesync"))['homesync_path'].should == "~/DB/HS" }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#sync' do
|
77
|
+
|
78
|
+
let(:command) { "sync" }
|
79
|
+
|
80
|
+
context "when argument is not under user's home" do
|
81
|
+
let(:args) { "/bin/bash" }
|
82
|
+
specify { stderr.should include("is not inside your home directory") }
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when argument doesn't exist" do
|
86
|
+
context "and matching file in homesync doesn't exist either" do
|
87
|
+
let(:args) { "~/404" }
|
88
|
+
specify { stderr.should include("doesn't exist") }
|
89
|
+
end
|
90
|
+
|
91
|
+
context "but matching file in homesync does" do
|
92
|
+
let(:args) { "~/todo.txt" }
|
93
|
+
let(:todo) { home.join("todo.txt") }
|
94
|
+
|
95
|
+
specify { stdout.should include("Created link to #{ENV['HOME']}/Dropbox/HomeSync/todo.txt") }
|
96
|
+
|
97
|
+
it "should create a link to the file in HomeSync" do
|
98
|
+
todo.readlink.should == homesync_path.join("todo.txt")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when argument is a link to the matching homesync file" do
|
104
|
+
let(:args) { "~/synced" }
|
105
|
+
specify { stderr.should include("is already syncing") }
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when argument is a link to somewhere else" do
|
109
|
+
let(:args) { "~/existing_link" }
|
110
|
+
specify { stderr.should include("is a symlink pointing somewhere else") }
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when argument is a regular file" do
|
114
|
+
context "and matching file in homesync doesn't exist" do
|
115
|
+
let(:args) { "~/Code/hello_world.rb" }
|
116
|
+
let(:original_file) { home.join("Code/hello_world.rb") }
|
117
|
+
let(:homesync_file) { homesync_path.join("Code/hello_world.rb") }
|
118
|
+
|
119
|
+
specify { stdout.should include("Moved #{original_file} to HomeSync and created a symlink to it") }
|
120
|
+
|
121
|
+
it "should move the original file to HomeSync" do
|
122
|
+
homesync_file.should be_file
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should create a link to the file in HomeSync" do
|
126
|
+
original_file.readlink.should == homesync_file
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "and matching file in homesync also exists" do
|
131
|
+
let(:args) { "~/bin/colors" }
|
132
|
+
let(:local_path) { home.join("bin/colors") }
|
133
|
+
let(:sync_path) { homesync_path.join("bin/colors") }
|
134
|
+
let(:contents) { File.read(local_path) }
|
135
|
+
|
136
|
+
context "without options" do
|
137
|
+
context "answering Yes to overwrite local" do
|
138
|
+
let(:stdin) { "y\n" }
|
139
|
+
|
140
|
+
specify { stdout.should include("Overwrite #{local_path}?") }
|
141
|
+
specify { stdout.should include("Replaced #{local_path} with symlink to #{sync_path}") }
|
142
|
+
|
143
|
+
it "should create a link to the file in HomeSync" do
|
144
|
+
local_path.readlink.should == sync_path
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should keep the HomeSync version of the file" do
|
148
|
+
contents.should include("with numbers")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "answering No to overwrite local and Yes to overwrite HomeSync" do
|
153
|
+
let(:stdin) { "n\ny\n" }
|
154
|
+
|
155
|
+
specify { stdout.should include("Overwrite #{local_path}?") }
|
156
|
+
specify { stdout.should include("Overwrite #{sync_path}?") }
|
157
|
+
specify { stdout.should include("Replaced #{sync_path} with local version and created symlink to it") }
|
158
|
+
|
159
|
+
it "should create a link to the file in HomeSync" do
|
160
|
+
local_path.readlink.should == sync_path
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should keep the local version of the file" do
|
164
|
+
contents.should include("with color names")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "answering No both to overwrite local and HomeSync" do
|
169
|
+
let(:stdin) { "n\nn\n" }
|
170
|
+
|
171
|
+
specify { stdout.should include("Overwrite #{local_path}?") }
|
172
|
+
specify { stdout.should include("Overwrite #{sync_path}?") }
|
173
|
+
specify { stdout.should include("Kept both versions, syncing has been cancelled") }
|
174
|
+
|
175
|
+
it "should keep the local file in place" do
|
176
|
+
contents.should include("with color names")
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should keep the HomeSync version of the file" do
|
180
|
+
File.read(sync_path).should include("with numbers")
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "using --overwrite-local option" do
|
186
|
+
let(:args) { "~/bin/colors --overwrite-local" }
|
187
|
+
|
188
|
+
specify { stdout.should include("Replaced #{local_path} with symlink to #{sync_path}") }
|
189
|
+
|
190
|
+
it "should create a link to the file in HomeSync" do
|
191
|
+
local_path.readlink.should == sync_path
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should keep the HomeSync version of the file" do
|
195
|
+
contents.should include("with numbers")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "using --overwrite-homesync option" do
|
200
|
+
let(:args) { "~/bin/colors --overwrite-homesync" }
|
201
|
+
|
202
|
+
specify { stdout.should include("Replaced #{sync_path} with local version and created symlink to it") }
|
203
|
+
|
204
|
+
it "should create a link to the file in HomeSync" do
|
205
|
+
local_path.readlink.should == sync_path
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should keep the local version of the file" do
|
209
|
+
contents.should include("with color names")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "using both --overwrite-local and --overwrite-homesync options" do
|
214
|
+
# this does not make any sense
|
215
|
+
let(:args) { "~/bin/colors --overwrite-local --overwrite-homesync" }
|
216
|
+
specify { stderr.should include("--overwrite-local and --overwrite-homesync cannot be used together") }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "when argument is a directory" do
|
222
|
+
context "and matching directory in homesync doesn't exist" do
|
223
|
+
let(:args) { "~/Code/hello_world" }
|
224
|
+
let(:original_dir) { home.join("Code/hello_world") }
|
225
|
+
let(:homesync_dir) { homesync_path.join("Code/hello_world") }
|
226
|
+
|
227
|
+
specify { stdout.should include("Moved #{original_dir} to HomeSync and created a symlink to it") }
|
228
|
+
|
229
|
+
it "should move the original directory to HomeSync" do
|
230
|
+
homesync_dir.should be_directory
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should create a link to the directory in HomeSync" do
|
234
|
+
original_dir.readlink.should == homesync_dir
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "and matching directory in homesync also exists" do
|
239
|
+
let(:args) { "~/tasks" }
|
240
|
+
let(:local_path) { home.join("tasks") }
|
241
|
+
let(:sync_path) { homesync_path.join("tasks") }
|
242
|
+
let(:dir_entries) { local_path.entries.map(&:basename).map(&:to_s) - %w(. ..) }
|
243
|
+
|
244
|
+
context "using --overwrite-local option" do
|
245
|
+
let(:args) { "~/tasks --overwrite-local" }
|
246
|
+
|
247
|
+
specify { stdout.should include("Replaced #{local_path} with symlink to #{sync_path}") }
|
248
|
+
|
249
|
+
it "should create a link to the directory in HomeSync" do
|
250
|
+
local_path.readlink.should == sync_path
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should keep the HomeSync version of the directory" do
|
254
|
+
dir_entries.should =~ %w(home work world_hunger)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
context "using --overwrite-homesync option" do
|
259
|
+
let(:args) { "~/tasks --overwrite-homesync" }
|
260
|
+
|
261
|
+
specify { stdout.should include("Replaced #{sync_path} with local version and created symlink to it") }
|
262
|
+
|
263
|
+
it "should create a link to the directory in HomeSync" do
|
264
|
+
local_path.readlink.should == sync_path
|
265
|
+
end
|
266
|
+
|
267
|
+
it "should keep the local version of the directory" do
|
268
|
+
dir_entries.should =~ %w(home garden shopping)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context "with custom homesync path" do
|
275
|
+
let(:contents) { File.read(home.join("custom_homesync")).chomp }
|
276
|
+
|
277
|
+
context "provided by the -p option" do
|
278
|
+
let(:args) { "~/custom_homesync -p ~/DB/HS" }
|
279
|
+
|
280
|
+
it "should create the link to the file in the custom homesync path" do
|
281
|
+
contents.should == "abbreviated"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context "stored in ~/.homesync configuration" do
|
286
|
+
let(:args) { "~/custom_homesync" }
|
287
|
+
|
288
|
+
before do
|
289
|
+
home.join(".homesync").open("w") {|f| f.write({'homesync_path' => "~/Dropbox/.homesync"}.to_yaml)}
|
290
|
+
@stdout, @stderr = capture_io { homesync "#{command} #{args}" }
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should create the link to the file in the custom homesync path" do
|
294
|
+
contents.should == "hidden"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'homesync'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.color_enabled = true
|
6
|
+
#config.filter_run :focus => true
|
7
|
+
|
8
|
+
def capture_io(stdin=nil)
|
9
|
+
begin
|
10
|
+
$stdin = StringIO.new(stdin) unless stdin.nil?
|
11
|
+
$stdout = StringIO.new
|
12
|
+
$stderr = StringIO.new
|
13
|
+
yield
|
14
|
+
result = [ $stdout.string, $stderr.string ]
|
15
|
+
ensure
|
16
|
+
$stdin = STDIN
|
17
|
+
$stdout = STDOUT
|
18
|
+
$stderr = STDERR
|
19
|
+
end
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
alias :silence :capture_io
|
24
|
+
end
|
25
|
+
|
26
|
+
def homesync(arguments)
|
27
|
+
HomeSync::CLI.start(arguments.split(/\s+/))
|
28
|
+
end
|
29
|
+
|
30
|
+
def setup_fixtures
|
31
|
+
tmp_path = Pathname.new(__FILE__).dirname.join("../tmp/test").expand_path
|
32
|
+
tmp_path.rmtree if tmp_path.exist?
|
33
|
+
tmp_path.join("Users").mkpath
|
34
|
+
ENV['HOME'] = "#{tmp_path}/Users/Alice"
|
35
|
+
fixtures = Pathname.new(__FILE__).dirname.join("fixtures/home")
|
36
|
+
%x{ cp -a #{fixtures} #{ENV['HOME']} }
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: homesync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.2'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Laszlo Bacsi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-02 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: &2152072400 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.14.6
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2152072400
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &2152070720 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.6.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2152070720
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: guard
|
38
|
+
requirement: &2152069240 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.8.3
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2152069240
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: guard-rspec
|
49
|
+
requirement: &2152067860 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.4.0
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2152067860
|
58
|
+
description: ! 'HomeSync makes it easy to synchronize any file under your home directory
|
59
|
+
|
60
|
+
with other machines through Dropbox by providing a simple commandline
|
61
|
+
|
62
|
+
interface for adding and removing symlinks to your files. Furthermore,
|
63
|
+
|
64
|
+
it knows how to handle several key directories correctly and how to
|
65
|
+
|
66
|
+
synchronize application preferences and data.
|
67
|
+
|
68
|
+
'
|
69
|
+
email: lackac@icanscale.com
|
70
|
+
executables:
|
71
|
+
- homesync
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- bin/homesync
|
76
|
+
- lib/homesync/cli.rb
|
77
|
+
- lib/homesync/version.rb
|
78
|
+
- lib/homesync.rb
|
79
|
+
- spec/fixtures/home/bin/colors
|
80
|
+
- spec/fixtures/home/Code/hello_world/app.rb
|
81
|
+
- spec/fixtures/home/Code/hello_world/config.ru
|
82
|
+
- spec/fixtures/home/Code/hello_world.rb
|
83
|
+
- spec/fixtures/home/DB/HS/custom_homesync
|
84
|
+
- spec/fixtures/home/Dropbox/HomeSync/bin/colors
|
85
|
+
- spec/fixtures/home/Dropbox/HomeSync/synced
|
86
|
+
- spec/fixtures/home/Dropbox/HomeSync/tasks/home
|
87
|
+
- spec/fixtures/home/Dropbox/HomeSync/tasks/work
|
88
|
+
- spec/fixtures/home/Dropbox/HomeSync/tasks/world_hunger
|
89
|
+
- spec/fixtures/home/Dropbox/HomeSync/todo.txt
|
90
|
+
- spec/fixtures/home/existing_link
|
91
|
+
- spec/fixtures/home/synced
|
92
|
+
- spec/fixtures/home/tasks/garden
|
93
|
+
- spec/fixtures/home/tasks/home
|
94
|
+
- spec/fixtures/home/tasks/shopping
|
95
|
+
- spec/fixtures/home/to/infinity/and/beyond
|
96
|
+
- spec/homesync/cli_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
- README.md
|
99
|
+
- LICENSE
|
100
|
+
- Gemfile
|
101
|
+
homepage: http://github.com/lackac/homesync
|
102
|
+
licenses: []
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
hash: 1059405670017922565
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: 1.3.6
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project: nowarning
|
124
|
+
rubygems_version: 1.8.7
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: HomeSync helps you synchronize your files with other machines through Dropbox.
|
128
|
+
test_files: []
|