entangler 0.4.1 → 1.0.0.beta1
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -0
- data/.travis.yml +17 -2
- data/README.md +12 -12
- data/Rakefile +5 -3
- data/bin/console +3 -3
- data/entangler.gemspec +15 -14
- data/exe/entangler +30 -19
- data/lib/entangler/entangled_file.rb +31 -102
- data/lib/entangler/executor/background/base.rb +79 -74
- data/lib/entangler/executor/background/master.rb +8 -3
- data/lib/entangler/executor/base.rb +19 -16
- data/lib/entangler/executor/master.rb +74 -24
- data/lib/entangler/executor/slave.rb +1 -1
- data/lib/entangler/version.rb +1 -1
- data/lib/entangler.rb +2 -2
- metadata +25 -25
- data/lib/entangler/executor/processing/base.rb +0 -122
- data/lib/notifier/README.md +0 -3
- data/lib/notifier/bin/darwin/notify +0 -0
- data/lib/notifier/bin/linux/notify +0 -0
- data/lib/notifier/src/darwin/BUILD +0 -7
- data/lib/notifier/src/darwin/README +0 -12
- data/lib/notifier/src/darwin/notify.c +0 -63
- data/lib/notifier/src/linux/BUILD +0 -4
- data/lib/notifier/src/linux/README +0 -18
- data/lib/notifier/src/linux/notify.c +0 -296
- data/lib/notifier/src/linux/old/BUILD +0 -11
- data/lib/notifier/src/linux/old/README +0 -14
- data/lib/notifier/src/linux/old/kernel-filesystem-monitor-daemon-cat.cpp +0 -165
- data/lib/notifier/src/linux/old/kernel-filesystem-monitor-daemon.cpp +0 -727
- data/lib/notifier/src/linux/old/kernel-filesystem-monitor-daemon.hh +0 -212
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 967e19ece113d7aa2359cf932a74e53ceb422ea6
|
4
|
+
data.tar.gz: 822f1cbd484e5a775de97f0d6bff6010312a4154
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5ba1871dc42e6d6aaf7616b8bc9b793b41d7fb150d1aaa501a453894ecb1fa0ad0c92d2e2385cad855a3c2f9ebf52a07100be2bc659ffc098e8717b0fe1e122
|
7
|
+
data.tar.gz: 46bcd6e8caa9fa1ae0a999dbe3a7aef0a579b073ef8886db943bda8686f340f297131dc503815b4326fc6bdc5604d00d4eab5f2c1a72df2b3fb501a4f7e28069
|
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
|
+
os:
|
4
|
+
- linux
|
5
|
+
- osx
|
3
6
|
rvm:
|
4
|
-
- 2.3.
|
5
|
-
|
7
|
+
- 2.3.3
|
8
|
+
- ruby-head
|
9
|
+
matrix:
|
10
|
+
include:
|
11
|
+
- os: osx
|
12
|
+
rvm: 2.2.5
|
13
|
+
- os: linux
|
14
|
+
rvm: 2.2.6
|
15
|
+
allow_failures:
|
16
|
+
- rvm: ruby-head
|
17
|
+
before_install:
|
18
|
+
- gem install bundler -v 1.13.6
|
19
|
+
install:
|
20
|
+
- bundle install --jobs=3 --retry=3
|
data/README.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# Entangler
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/daveallie/entangler)
|
4
4
|
|
5
|
-
|
6
|
-
- librsync 2.x
|
5
|
+
Syncing tool used to keep a local and remote (over SSH) folder in sync.
|
7
6
|
|
8
7
|
## Installation
|
9
8
|
|
@@ -19,27 +18,29 @@ $ entangler master /some/base/path user@remote:/some/remote/path
|
|
19
18
|
|
20
19
|
```
|
21
20
|
$ entangler -h
|
22
|
-
Entangler
|
21
|
+
Entangler v1.0.0.beta1
|
23
22
|
|
24
23
|
Usage:
|
25
24
|
entangler master <base_dir> <remote_user>@<remote_host>:<remote_base_dir> [options]
|
26
25
|
entangler master <base_dir> <other_synced_base_dir> [options]
|
27
26
|
|
28
27
|
Options:
|
29
|
-
-i, --ignore '
|
30
|
-
All
|
28
|
+
-i, --ignore '.git' Ignore path when syncing, string is regex if surrounded by '/'
|
29
|
+
All paths should be relative to the base sync directory.
|
31
30
|
-p, --port PORT Overwrite the SSH port (usually 22)
|
32
31
|
(doesn't do anything in slave mode)
|
33
|
-
-v, --
|
32
|
+
-v, --verbose Log Debug lines
|
33
|
+
--version Show version number
|
34
34
|
-h, --help Show this message
|
35
35
|
```
|
36
36
|
|
37
|
-
### Ignoring folders
|
37
|
+
### Ignoring files and folders
|
38
38
|
|
39
|
-
If you specify string, instead of regex, it will match any
|
40
|
-
If you want to just ignore the the `.git` sub-directories but not the content in the git folder, you
|
39
|
+
If you specify a string, instead of a regex, it will match any path starting with that string, i.e. `-i '.git'` will ignore the `.git`
|
40
|
+
folder and all its sub-directories. If you want to just ignore the the `.git` sub-directories but not the content in the git folder, you'll
|
41
|
+
have to use regex. `-i '/^\.git\/[^\/]+\/.*/'` will match all sub-directories of `.git/`, but not the files in `.git`.
|
41
42
|
|
42
|
-
You can specify multiple `-i` or `--ignore` flags to ignore multiple
|
43
|
+
You can specify multiple `-i` or `--ignore` flags to ignore multiple paths.
|
43
44
|
|
44
45
|
## Development
|
45
46
|
|
@@ -51,7 +52,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
51
52
|
|
52
53
|
Bug reports and pull requests are welcome on GitHub at https://github.com/daveallie/entangler. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
53
54
|
|
54
|
-
|
55
55
|
## License
|
56
56
|
|
57
57
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
3
4
|
|
5
|
+
RuboCop::RakeTask.new(:rubocop)
|
4
6
|
RSpec::Core::RakeTask.new(:spec)
|
5
7
|
|
6
|
-
task :
|
8
|
+
task default: [:rubocop, :spec]
|
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'entangler'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "entangler"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start
|
data/entangler.gemspec
CHANGED
@@ -4,24 +4,25 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'entangler/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'entangler'
|
8
8
|
spec.version = Entangler::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Dave Allie']
|
10
|
+
spec.email = ['dave@daveallie.com']
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
12
|
+
spec.summary = 'Two way file syncer using platform native notify.'
|
13
|
+
spec.description = 'Two way file syncer using platform native notify.'
|
14
|
+
spec.homepage = 'https://github.com/daveallie/entangler'
|
15
|
+
spec.license = 'MIT'
|
16
16
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
-
spec.bindir =
|
18
|
+
spec.bindir = 'exe'
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.
|
26
|
-
spec.add_dependency
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.12'
|
23
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
25
|
+
spec.add_development_dependency 'rubocop', '~> 0.46'
|
26
|
+
spec.add_dependency 'listen', '~> 3.1'
|
27
|
+
spec.add_dependency 'to_regexp', '~> 0.2'
|
27
28
|
end
|
data/exe/entangler
CHANGED
@@ -5,59 +5,60 @@ require 'to_regexp'
|
|
5
5
|
|
6
6
|
options = {}
|
7
7
|
OptionParser.new do |opts|
|
8
|
-
opts.banner = %
|
8
|
+
opts.banner = %(Entangler v#{Entangler::VERSION}
|
9
9
|
|
10
10
|
Usage:
|
11
11
|
entangler master <base_dir> <remote_user>@<remote_host>:<remote_base_dir> [options]
|
12
12
|
entangler master <base_dir> <other_synced_base_dir> [options])
|
13
13
|
|
14
|
-
opts.separator
|
15
|
-
opts.separator
|
14
|
+
opts.separator ''
|
15
|
+
opts.separator 'Options:'
|
16
16
|
|
17
|
-
opts.on(
|
18
|
-
|
17
|
+
opts.on('-i', "--ignore '.git'", "Ignore path when syncing, string is regex if surrounded by '/'",
|
18
|
+
'All paths should be relative to the base sync directory.') do |ignore|
|
19
19
|
options[:ignore] ||= []
|
20
20
|
options[:ignore] << ignore
|
21
21
|
end
|
22
22
|
|
23
|
-
opts.on(
|
23
|
+
opts.on('-p', '--port PORT', 'Overwrite the SSH port (usually 22)', "(doesn't do anything in slave mode)") do |port|
|
24
24
|
options[:port] = port
|
25
25
|
end
|
26
26
|
|
27
|
-
opts.
|
27
|
+
opts.on('-v', '--verbose', 'Log Debug lines') do
|
28
|
+
options[:verbose] = true
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on_tail('--version', 'Show version number') do
|
28
32
|
puts Entangler::VERSION
|
29
33
|
exit
|
30
34
|
end
|
31
35
|
|
32
|
-
opts.on_tail(
|
36
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
33
37
|
puts opts
|
34
38
|
exit
|
35
39
|
end
|
36
40
|
end.parse!
|
37
41
|
|
38
42
|
mode = ARGV.shift
|
39
|
-
unless mode &&
|
43
|
+
unless mode && %w(master slave).include?(mode)
|
40
44
|
puts "Mode unknown, please read help:\nentangler -h"
|
41
45
|
exit 1
|
42
46
|
end
|
43
47
|
|
44
48
|
base_dir = ARGV.shift
|
45
49
|
unless base_dir
|
46
|
-
puts
|
50
|
+
puts 'Missing base directory'
|
47
51
|
exit 1
|
48
52
|
end
|
49
53
|
|
50
54
|
if mode == 'master'
|
51
55
|
remote_information = ARGV.shift
|
52
56
|
|
53
|
-
unless remote_information
|
54
|
-
raise 'Missing destination information'
|
55
|
-
exit 1
|
56
|
-
end
|
57
|
+
raise 'Missing destination information' unless remote_information
|
57
58
|
|
58
59
|
user = host = path = error = nil
|
59
60
|
remote_mode = false
|
60
|
-
if remote_information
|
61
|
+
if remote_information =~ /[^@]+@[^:]+:.+/
|
61
62
|
remote_mode = true
|
62
63
|
user, rest = remote_information.split('@', 2)
|
63
64
|
host, path = (rest || '').split(':', 2)
|
@@ -74,17 +75,27 @@ if mode == 'master'
|
|
74
75
|
exit 1
|
75
76
|
end
|
76
77
|
|
77
|
-
opts = {remote_base_dir: path, remote_mode: remote_mode}
|
78
|
-
|
78
|
+
opts = { remote_base_dir: path, remote_mode: remote_mode }
|
79
|
+
if remote_mode
|
80
|
+
opts[:remote_user] = user
|
81
|
+
opts[:remote_host] = host
|
82
|
+
end
|
79
83
|
opts[:remote_port] = options[:port] if options[:port]
|
80
84
|
else
|
81
|
-
opts = {mode: 'slave'}
|
85
|
+
opts = { mode: 'slave' }
|
82
86
|
end
|
83
87
|
|
84
88
|
if options[:ignore]
|
85
89
|
opts[:ignore] = options[:ignore].map do |opt|
|
86
|
-
|
90
|
+
if ToRegexp::String.literal? opt
|
91
|
+
source, *rest = opt.as_regexp(detect: true)
|
92
|
+
::Regexp.new "^#{source}", *rest
|
93
|
+
else
|
94
|
+
opt.to_regexp(detect: true)
|
95
|
+
end
|
87
96
|
end
|
88
97
|
end
|
89
98
|
|
99
|
+
opts[:verbose] = options[:verbose]
|
100
|
+
|
90
101
|
Entangler.run(base_dir, opts)
|
@@ -1,23 +1,15 @@
|
|
1
|
-
require '
|
2
|
-
require 'tempfile'
|
1
|
+
require 'fileutils'
|
3
2
|
|
4
3
|
module Entangler
|
5
4
|
class EntangledFile
|
6
|
-
|
7
|
-
|
8
|
-
# 2: delta loaded
|
9
|
-
attr_accessor :state
|
10
|
-
attr_accessor :desired_modtime
|
11
|
-
attr_reader :path
|
5
|
+
attr_accessor :desired_modtime, :action
|
6
|
+
attr_reader :path, :contents
|
12
7
|
|
13
|
-
def initialize(rel_path)
|
8
|
+
def initialize(action, rel_path)
|
9
|
+
@action = action
|
14
10
|
@path = rel_path
|
15
|
-
@state = 0
|
16
11
|
@desired_modtime = Time.now.to_i
|
17
|
-
|
18
|
-
|
19
|
-
def done?
|
20
|
-
@state == 2
|
12
|
+
@contents = nil
|
21
13
|
end
|
22
14
|
|
23
15
|
def full_path
|
@@ -25,116 +17,53 @@ module Entangler
|
|
25
17
|
end
|
26
18
|
|
27
19
|
def file_exists?
|
28
|
-
File.
|
20
|
+
File.exist?(full_path)
|
29
21
|
end
|
30
22
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
temp_empty_file = Tempfile.new('empty_file')
|
38
|
-
LibRubyDiff.patch(temp_empty_file.path, delta_file.path, tempfile.path)
|
23
|
+
def process
|
24
|
+
if action == :create || action == :update
|
25
|
+
create_parent_directory
|
26
|
+
write_contents
|
27
|
+
elsif action == :delete
|
28
|
+
delete_file
|
39
29
|
end
|
40
|
-
tempfile.rewind
|
41
|
-
File.open(full_path, 'w'){|f| f.write(tempfile.read)}
|
42
|
-
tempfile.close
|
43
|
-
tempfile.unlink
|
44
|
-
File.utime(File.atime(full_path), @desired_modtime, full_path)
|
45
30
|
end
|
46
31
|
|
47
32
|
private
|
48
|
-
def signature_exists?
|
49
|
-
defined?(@signature_tempfile)
|
50
|
-
end
|
51
33
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
34
|
+
def create_parent_directory
|
35
|
+
dirname = File.dirname(full_path)
|
36
|
+
if File.exist?(dirname)
|
37
|
+
unless File.directory?(dirname)
|
38
|
+
FileUtils.rm dirname
|
39
|
+
FileUtils.mkdir_p dirname
|
40
|
+
end
|
57
41
|
else
|
58
|
-
|
59
|
-
LibRubyDiff.signature(temp_empty_file.path, @signature_tempfile.path)
|
42
|
+
FileUtils.mkdir_p dirname
|
60
43
|
end
|
61
|
-
@signature_tempfile.rewind
|
62
|
-
@signature_tempfile
|
63
|
-
end
|
64
|
-
|
65
|
-
def write_signature(contents)
|
66
|
-
@signature_tempfile = Tempfile.new('sig_file')
|
67
|
-
@signature_tempfile.write(contents)
|
68
|
-
@signature_tempfile.rewind
|
69
|
-
end
|
70
|
-
|
71
|
-
def signature
|
72
|
-
signature_file.read
|
73
|
-
end
|
74
|
-
|
75
|
-
def delta_exists?
|
76
|
-
defined?(@delta_tempfile)
|
77
|
-
end
|
78
|
-
|
79
|
-
def delta_file
|
80
|
-
return @delta_tempfile if delta_exists?
|
81
|
-
raise "Signature file doesn't exist when creaing delta" unless signature_exists?
|
82
|
-
|
83
|
-
@delta_tempfile = Tempfile.new('delta_file')
|
84
|
-
LibRubyDiff.delta(full_path, signature_file.path, @delta_tempfile.path)
|
85
|
-
@delta_tempfile.rewind
|
86
|
-
@delta_tempfile
|
87
44
|
end
|
88
45
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
|
46
|
+
def write_contents
|
47
|
+
delete_file if file_exists? && File.directory?(full_path)
|
48
|
+
File.open(full_path, 'w') { |f| f.write(contents) }
|
49
|
+
File.utime(File.atime(full_path), desired_modtime, full_path)
|
93
50
|
end
|
94
51
|
|
95
|
-
def
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
def close_and_unlink_files
|
100
|
-
if signature_exists?
|
101
|
-
@signature_tempfile.close
|
102
|
-
@signature_tempfile.unlink
|
103
|
-
@signature_tempfile = nil
|
104
|
-
end
|
105
|
-
|
106
|
-
if delta_exists?
|
107
|
-
@delta_tempfile.close
|
108
|
-
@delta_tempfile.unlink
|
109
|
-
@delta_tempfile = nil
|
110
|
-
end
|
52
|
+
def delete_file
|
53
|
+
FileUtils.rm_rf(full_path) if file_exists?
|
111
54
|
end
|
112
55
|
|
113
56
|
def marshal_dump
|
114
|
-
|
115
|
-
|
116
|
-
if @state == 0
|
117
|
-
last_arg = signature_file.read
|
118
|
-
@state = 1
|
119
|
-
elsif @state == 1
|
57
|
+
if file_exists? && (action == :create || action == :update)
|
120
58
|
@desired_modtime = File.mtime(full_path).to_i
|
121
|
-
|
122
|
-
@state = 2
|
59
|
+
@contents = File.read(full_path)
|
123
60
|
end
|
124
61
|
|
125
|
-
|
126
|
-
|
127
|
-
[@path, @state, @desired_modtime, last_arg]
|
62
|
+
[action, path, desired_modtime, contents]
|
128
63
|
end
|
129
64
|
|
130
65
|
def marshal_load(array)
|
131
|
-
@
|
132
|
-
|
133
|
-
if @state == 1
|
134
|
-
write_signature(last_arg)
|
135
|
-
elsif @state == 2
|
136
|
-
write_delta(last_arg)
|
137
|
-
end
|
66
|
+
@action, @path, @desired_modtime, @contents = *array
|
138
67
|
end
|
139
68
|
end
|
140
69
|
end
|
@@ -1,106 +1,111 @@
|
|
1
|
+
require 'listen'
|
2
|
+
require 'entangler/entangled_file'
|
3
|
+
|
1
4
|
module Entangler
|
2
5
|
module Executor
|
3
6
|
module Background
|
4
7
|
module Base
|
5
8
|
protected
|
6
|
-
def wait_for_threads
|
7
|
-
@consumer_thread.join
|
8
|
-
@remote_io_thread.join
|
9
|
-
@local_io_thread.join
|
10
|
-
Process.wait @notify_daemon_pid
|
11
|
-
end
|
12
9
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
@remote_io_thread.terminate
|
17
|
-
@local_io_thread.terminate
|
10
|
+
def start_listener
|
11
|
+
logger.info('starting listener')
|
12
|
+
listener.start
|
18
13
|
end
|
19
14
|
|
20
|
-
def
|
21
|
-
|
22
|
-
r,w = IO.pipe
|
23
|
-
@notify_daemon_pid = spawn(start_notify_daemon_cmd, out: w)
|
24
|
-
w.close
|
25
|
-
@notify_reader = r
|
15
|
+
def stop_listener
|
16
|
+
listener.stop
|
26
17
|
end
|
27
18
|
|
28
19
|
def start_remote_io
|
29
20
|
logger.info('starting remote IO')
|
30
21
|
@remote_io_thread = Thread.new do
|
31
|
-
|
22
|
+
with_kill_threads_rescue do
|
32
23
|
loop do
|
33
24
|
msg = Marshal.load(@remote_reader)
|
34
|
-
|
35
|
-
|
36
|
-
case msg[:type]
|
37
|
-
when :new_changes
|
38
|
-
process_new_changes(msg[:content])
|
39
|
-
when :entangled_files
|
40
|
-
process_entangled_files(msg[:content])
|
41
|
-
end
|
25
|
+
process_remote_changes(msg)
|
42
26
|
end
|
43
|
-
rescue => e
|
44
|
-
$stderr.puts e.message
|
45
|
-
$stderr.puts e.backtrace.join("\n")
|
46
|
-
kill_off_threads
|
47
27
|
end
|
48
28
|
end
|
49
29
|
end
|
50
30
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
rescue => e
|
68
|
-
$stderr.puts e.message
|
69
|
-
$stderr.puts e.backtrace.join("\n")
|
70
|
-
kill_off_threads
|
31
|
+
def wait_for_threads
|
32
|
+
@remote_io_thread.join
|
33
|
+
end
|
34
|
+
|
35
|
+
def kill_off_threads
|
36
|
+
@remote_io_thread.terminate
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def listener
|
42
|
+
@listener ||= begin
|
43
|
+
l = Listen::Listener.new(base_dir) do |modified, added, removed|
|
44
|
+
process_local_changes(generate_entangled_files(added, :create) +
|
45
|
+
generate_entangled_files(modified, :update) +
|
46
|
+
generate_entangled_files(removed, :delete))
|
71
47
|
end
|
48
|
+
l.ignore!(@opts[:ignore])
|
49
|
+
l
|
72
50
|
end
|
73
51
|
end
|
74
52
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
process_lines(msg.uniq)
|
93
|
-
msg = []
|
94
|
-
sleep 0.5
|
53
|
+
def generate_entangled_files(paths, action)
|
54
|
+
paths.map { |path| Entangler::EntangledFile.new(action, strip_base_path(path)) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove_recently_changed_files(entangled_files)
|
58
|
+
@recently_received_paths.select! { |_, time| Time.now.to_f < time + 0.5 }
|
59
|
+
paths = @recently_received_paths.map(&:first)
|
60
|
+
entangled_files.reject { |ef| paths.include?(ef.path) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_local_changes(changes)
|
64
|
+
with_listener_pause(0) do
|
65
|
+
changes = remove_recently_changed_files(changes)
|
66
|
+
if changes.any?
|
67
|
+
logger.info("PROCESSING #{changes.length} local changes")
|
68
|
+
logger.debug(changes.map(&:path).join("\n"))
|
69
|
+
send_to_remote(changes)
|
95
70
|
end
|
96
71
|
end
|
97
72
|
end
|
98
73
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
74
|
+
def process_remote_changes(changes)
|
75
|
+
with_listener_pause(1) do
|
76
|
+
return if changes.nil?
|
77
|
+
logger.info("PROCESSING #{changes.length} remote changes")
|
78
|
+
logger.debug(changes.map(&:path).join("\n"))
|
79
|
+
changes.each(&:process)
|
80
|
+
update_recently_received_paths(changes)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def update_recently_received_paths(changes)
|
85
|
+
changes.each do |change|
|
86
|
+
index = @recently_received_paths.index { |path, _| path == change.path }
|
87
|
+
if index.nil?
|
88
|
+
@recently_received_paths << [change.path, Time.now.to_f]
|
89
|
+
else
|
90
|
+
@recently_received_paths[index][1] = Time.now.to_f
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def with_kill_threads_rescue
|
96
|
+
yield
|
97
|
+
rescue => e
|
98
|
+
$stderr.puts e.message
|
99
|
+
$stderr.puts e.backtrace.join("\n")
|
100
|
+
kill_off_threads
|
101
|
+
end
|
102
102
|
|
103
|
-
|
103
|
+
def with_listener_pause(idx)
|
104
|
+
@listener_pauses[idx] = true
|
105
|
+
listener.pause
|
106
|
+
yield
|
107
|
+
@listener_pauses[idx] = false
|
108
|
+
listener.start if @listener_pauses.none?
|
104
109
|
end
|
105
110
|
end
|
106
111
|
end
|