homesync 0.2
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/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: []
|