entangler 1.0.1 → 1.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c37e01ff82caac966cc6914e8ce9b3ae76820106
4
- data.tar.gz: eb49a80d236d3a4ea41aae48b354de2be624e567
2
+ SHA256:
3
+ metadata.gz: 04a6d5ab6db60640272b7acd3922ba845d2ab171ee4001f4be0b753b7d151c28
4
+ data.tar.gz: 962a2ab010fffe236dea92a6f5eba83d0ade41ce4edb9af0f867054d2923279c
5
5
  SHA512:
6
- metadata.gz: e652cb24a4724fa59c4c68f687a7e36cb5a63ecf4f59fa23ab42563fefd0e6e2c500c0f2a55ebfa9aca01b4fb4908443b093d3f63a2188198aeefd392997a36e
7
- data.tar.gz: b0f8dd950f5a0946f9738db12f43a41a44b313034ec4bc0b13362cdce3c3b04a638ea908b0a730d735b3fa4533d271eb6a1b8095308069a4220d1569728a3763
6
+ metadata.gz: 3db61680fd1d006a4ce4dd9a1559c95090c74c898e8c4668378eb4ee5e440f919960104b1d97a62e14846214619e825f16f00385a72fdc4aae05b70dc565ad34
7
+ data.tar.gz: 65567038136a7bb19280347c8f1a82a59e5b40e0627d4be1ac79f54057c73f00a34b0a91ac8c81e2996a1e646e5649b989122a013db2aaf6377a4109ff333743
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in entangler.gemspec
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- # Entangler
1
+ <p align="center" style="width: 50px;"><img src="https://vignette1.wikia.nocookie.net/pokemon/images/e/ef/114Tangela_Dream.png/revision/latest/scale-to-width-down/185?cb=20141203054028"/></p>
2
+ <h1 align="center">Entangler</h1>
2
3
 
3
4
  [![Build Status](https://travis-ci.org/daveallie/entangler.svg?branch=master)](https://travis-ci.org/daveallie/entangler)
4
5
 
@@ -18,7 +19,7 @@ $ entangler master /some/base/path user@remote:/some/remote/path
18
19
 
19
20
  ```
20
21
  $ entangler -h
21
- Entangler v1.0.1
22
+ Entangler v1.2.0
22
23
 
23
24
  Usage:
24
25
  entangler master <base_dir> <remote_user>@<remote_host>:<remote_base_dir> [options]
@@ -30,14 +31,15 @@ Options:
30
31
  -p, --port PORT Overwrite the SSH port (usually 22)
31
32
  (doesn't do anything in slave mode)
32
33
  -v, --verbose Log Debug lines
34
+ -q, --quiet Don't log to stdout in master process
33
35
  --version Show version number
34
36
  -h, --help Show this message
35
37
  ```
36
38
 
37
39
  ### Ignoring files and folders
38
40
 
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
+ 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`
42
+ 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
43
  have to use regex. `-i '/^\.git(?:\/[^\/]+){2,}$/'` will match all sub-directories of `.git/`, but not the files in `.git`.
42
44
 
43
45
  You can specify multiple `-i` or `--ignore` flags to ignore multiple paths.
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'entangler/version'
5
6
 
@@ -14,15 +15,19 @@ Gem::Specification.new do |spec|
14
15
  spec.homepage = 'https://github.com/daveallie/entangler'
15
16
  spec.license = 'MIT'
16
17
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.files = Dir['lib/**/*'] + Dir['exe/**/*'] + %w[CODE_OF_CONDUCT.md LICENSE.txt README.md
19
+ Gemfile entangler.gemspec]
18
20
  spec.bindir = 'exe'
19
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.executables = ['entangler']
20
22
  spec.require_paths = ['lib']
23
+ spec.required_ruby_version = '>= 2.5'
21
24
 
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'
25
+ spec.add_development_dependency 'bundler', '>= 2.1'
26
+ spec.add_development_dependency 'rake', '>= 13.0.1'
27
+ spec.add_development_dependency 'rspec', '>= 3.9'
28
+ spec.add_development_dependency 'rubocop', '~> 1.6'
29
+ spec.add_development_dependency 'rubocop-rake', '~> 0.5.1'
30
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.0'
31
+ spec.add_dependency 'listen', '~> 3.3'
32
+ spec.add_dependency 'to_regexp', '~> 0.2.1'
28
33
  end
@@ -1,8 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'entangler'
3
5
  require 'optparse'
4
6
  require 'to_regexp'
5
7
 
8
+ def bool_opt(options, opts, key, *args)
9
+ opts.on(*args) do
10
+ options[key] = true
11
+ end
12
+ end
13
+
14
+ def value_opt(options, opts, key, *args)
15
+ opts.on(*args) do |val|
16
+ options[key] = val
17
+ end
18
+ end
19
+
6
20
  options = {}
7
21
  OptionParser.new do |opts|
8
22
  opts.banner = %(Entangler v#{Entangler::VERSION}
@@ -20,13 +34,10 @@ Usage:
20
34
  options[:ignore] << ignore
21
35
  end
22
36
 
23
- opts.on('-p', '--port PORT', 'Overwrite the SSH port (usually 22)', "(doesn't do anything in slave mode)") do |port|
24
- options[:port] = port
25
- end
26
-
27
- opts.on('-v', '--verbose', 'Log Debug lines') do
28
- options[:verbose] = true
29
- end
37
+ value_opt(options, opts, :port, '-p', '--port PORT', 'Overwrite the SSH port (usually 22)',
38
+ "(doesn't do anything in slave mode)")
39
+ bool_opt(options, opts, :verbose, '-v', '--verbose', 'Log Debug lines')
40
+ bool_opt(options, opts, :quiet, '-q', '--quiet', "Don't log to stdout in master process")
30
41
 
31
42
  opts.on_tail('--version', 'Show version number') do
32
43
  puts Entangler::VERSION
@@ -40,7 +51,7 @@ Usage:
40
51
  end.parse!
41
52
 
42
53
  mode = ARGV.shift
43
- unless mode && %w(master slave).include?(mode)
54
+ unless mode && %w[master slave].include?(mode)
44
55
  puts "Mode unknown, please read help:\nentangler -h"
45
56
  exit 1
46
57
  end
@@ -90,9 +101,7 @@ end
90
101
 
91
102
  if options[:ignore]
92
103
  opts[:ignore] = options[:ignore].map do |opt|
93
- if opt.start_with?('"') && opt.end_with?('"') || opt.start_with?("'") && opt.end_with?("'")
94
- opt = opt[1..-2]
95
- end
104
+ opt = opt[1..-2] if opt.start_with?('"') && opt.end_with?('"') || opt.start_with?("'") && opt.end_with?("'")
96
105
 
97
106
  if ToRegexp::String.literal? opt
98
107
  source, *rest = opt.as_regexp(detect: true)
@@ -103,6 +112,12 @@ if options[:ignore]
103
112
  end
104
113
  end
105
114
 
115
+ opts[:quiet] = options[:quiet]
106
116
  opts[:verbose] = options[:verbose]
107
117
 
108
- Entangler.run(base_dir, opts)
118
+ begin
119
+ Entangler.run(base_dir, opts)
120
+ rescue Entangler::EntanglerError => e
121
+ puts e.message
122
+ exit 1
123
+ end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'entangler/version'
2
4
  require_relative 'entangler/errors'
5
+ require_relative 'entangler/logger'
3
6
  require_relative 'entangler/entangled_file'
4
7
 
5
8
  module Entangler
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
 
3
5
  module Entangler
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Entangler
2
4
  class EntanglerError < StandardError; end
3
5
  class ValidationError < EntanglerError; end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'listen'
2
4
  require 'entangler/entangled_file'
3
5
  require 'benchmark'
@@ -56,6 +58,7 @@ module Entangler
56
58
  def remove_recently_changed_files(entangled_files)
57
59
  @recently_received_paths.select! { |_, time| Time.now.to_f < time + 0.5 }
58
60
  paths = @recently_received_paths.map(&:first)
61
+ logger.debug("Skipping paths #{paths.join(', ')} as they have changed recently") if paths.any?
59
62
  entangled_files.reject { |ef| paths.include?(ef.path) }
60
63
  end
61
64
 
@@ -75,6 +78,7 @@ module Entangler
75
78
  def process_remote_changes(changes)
76
79
  with_listener_pause(1) do
77
80
  return if changes.nil?
81
+
78
82
  logger.info("Processing - #{changes.length} remote changes")
79
83
  logger.debug("File List:\n#{changes.map(&:path).join("\n")}")
80
84
  with_log_time("Completed - #{changes.length} remote changes") do
@@ -97,9 +101,9 @@ module Entangler
97
101
 
98
102
  def with_kill_threads_rescue
99
103
  yield
100
- rescue => e
101
- $stderr.puts e.message
102
- $stderr.puts e.backtrace.join("\n")
104
+ rescue StandardError => e
105
+ warn e.message
106
+ warn e.backtrace.join("\n")
103
107
  kill_off_threads
104
108
  end
105
109
 
@@ -107,6 +111,7 @@ module Entangler
107
111
  @listener_pauses[idx] = true
108
112
  listener.pause
109
113
  yield
114
+ ensure
110
115
  @listener_pauses[idx] = false
111
116
  listener.start if @listener_pauses.none?
112
117
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'open3'
2
4
 
3
5
  module Entangler
@@ -21,7 +23,7 @@ module Entangler
21
23
  super
22
24
  begin
23
25
  Process.wait @remote_thread[:pid]
24
- rescue
26
+ rescue StandardError
25
27
  nil
26
28
  end
27
29
  end
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
  require 'fileutils'
3
- require 'thread'
4
5
  require_relative 'background/base'
6
+ require_relative 'validation/base'
5
7
 
6
8
  module Entangler
7
9
  module Executor
8
10
  class Base
9
11
  include Entangler::Executor::Background::Base
12
+ include Entangler::Executor::Validation::Base
10
13
 
11
14
  attr_reader :base_dir
12
15
 
@@ -20,6 +23,7 @@ module Entangler
20
23
  @opts[:ignore] << /^\.entangler.*/
21
24
 
22
25
  validate_opts
26
+ Entangler::Logger.create_log_dir(base_dir)
23
27
  end
24
28
 
25
29
  def generate_abs_path(rel_path)
@@ -44,36 +48,16 @@ module Entangler
44
48
 
45
49
  protected
46
50
 
47
- def validate_opts; end
48
-
49
51
  def send_to_remote(msg = {})
50
52
  Marshal.dump(msg, @remote_writer)
51
53
  end
52
54
 
53
55
  def logger
54
- FileUtils.mkdir_p log_dir
55
- @logger ||= begin
56
- l = Logger.new(File.join(log_dir, 'entangler.log'))
57
- l.level = @opts[:verbose] ? Logger::DEBUG : Logger::INFO
58
- l.formatter = logger_formatter
59
- l
60
- end
61
- end
62
-
63
- def logger_formatter
64
- proc do |severity, datetime, _, msg|
65
- date_format = datetime.strftime('%Y-%m-%d %H:%M:%S')
66
- "[#{date_format}] #{severity.rjust(5)}: #{msg}\n"
67
- end
68
- end
69
-
70
- def log_dir
71
- File.join(base_dir, '.entangler', 'log')
56
+ @logger ||= Entangler::Logger.new(log_outputs, @opts[:verbose])
72
57
  end
73
58
 
74
- def validate_base_dir(base_dir)
75
- raise Entangler::ValidationError, "Base directory doesn't exist" unless File.exist?(base_dir)
76
- raise Entangler::ValidationError, 'Base directory is a file' unless File.directory?(base_dir)
59
+ def log_outputs
60
+ [Entangler::Logger.log_file_path(base_dir)]
77
61
  end
78
62
  end
79
63
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Entangler
4
+ module Helper
5
+ def self.with_temp_file(name: 'tmp_file', contents: nil)
6
+ require 'tempfile'
7
+
8
+ t = Tempfile.new(name)
9
+ t.puts(contents) unless contents.nil?
10
+ t.close
11
+ yield t
12
+ t.unlink
13
+ end
14
+ end
15
+ end
@@ -1,9 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
1
4
  require_relative 'background/master'
5
+ require_relative 'validation/master'
2
6
 
3
7
  module Entangler
4
8
  module Executor
5
9
  class Master < Base
6
10
  include Entangler::Executor::Background::Master
11
+ include Entangler::Executor::Validation::Master
7
12
 
8
13
  def run
9
14
  perform_initial_rsync
@@ -16,64 +21,17 @@ module Entangler
16
21
 
17
22
  private
18
23
 
19
- def validate_opts
20
- super
21
- if @opts[:remote_mode]
22
- @opts[:remote_port] ||= '22'
23
- validate_remote_opts
24
- else
25
- validate_local_opts
26
- end
27
- end
28
-
29
- def validate_local_opts
30
- unless File.exist?(@opts[:remote_base_dir])
31
- raise Entangler::ValidationError, "Destination directory doesn't exist"
32
- end
33
- unless File.directory?(@opts[:remote_base_dir])
34
- raise Entangler::ValidationError, 'Destination directory is a file'
35
- end
36
- @opts[:remote_base_dir] = File.realpath(File.expand_path(@opts[:remote_base_dir]))
37
- return unless @opts[:remote_base_dir] == base_dir
38
- raise Entangler::ValidationError, "Destination directory can't be the same as the base directory"
39
- end
40
-
41
- def validate_remote_opts
42
- keys = @opts.keys
43
- raise Entangler::ValidationError, 'Missing remote base dir' unless keys.include?(:remote_base_dir)
44
- raise Entangler::ValidationError, 'Missing remote user' unless keys.include?(:remote_user)
45
- raise Entangler::ValidationError, 'Missing remote host' unless keys.include?(:remote_host)
46
- validate_remote_base_dir
47
- validate_remote_entangler_version
48
- end
49
-
50
- def validate_remote_base_dir
51
- res = `#{generate_ssh_command("[[ -d '#{@opts[:remote_base_dir]}' ]] && echo 'ok' || echo 'missing'")}`
52
- raise Entangler::ValidationError, 'Cannot connect to remote' if res.empty?
53
- raise Entangler::ValidationError, 'Remote base dir invalid' unless res.strip == 'ok'
54
- end
55
-
56
- def validate_remote_entangler_version
57
- return unless @opts[:remote_mode]
58
- res = `#{generate_ssh_command('source ~/.rvm/environments/default && entangler --version')}`
59
- remote_version = Gem::Version.new(res.strip)
60
- local_version = Gem::Version.new(Entangler::VERSION)
61
- return unless major_version_mismatch?(local_version, remote_version)
62
- msg = 'Entangler version too far apart, please update either local or remote Entangler.' \
63
- " Local version is #{local_version} and remote version is #{remote_version}."
64
- raise Entangler::VersionMismatchError, msg
65
- end
66
-
67
- def major_version_mismatch?(version1, version2)
68
- version1.segments[0] != version2.segments[0] ||
69
- (version1.segments[0].zero? && version1 != version2) ||
70
- ((version1.prerelease? || version2.prerelease?) && version1 != version2)
24
+ def log_outputs
25
+ outs = [Entangler::Logger.log_file_path(base_dir)]
26
+ outs << $stdout unless @opts[:quiet]
71
27
  end
72
28
 
73
29
  def perform_initial_rsync
74
30
  logger.info 'Running initial sync'
75
- IO.popen(rsync_cmd_string).each do |line|
76
- logger.debug line.chomp
31
+ with_temp_rsync_ignores do |file_path|
32
+ IO.popen(rsync_cmd_string(file_path)).each do |line|
33
+ logger.debug line.chomp
34
+ end
77
35
  end
78
36
  logger.debug 'Initial sync complete'
79
37
  end
@@ -107,12 +65,17 @@ module Entangler
107
65
  "#{@opts[:remote_user]}@#{@opts[:remote_host]}"
108
66
  end
109
67
 
110
- def rsync_cmd_string
111
- exclude_args = find_rsync_ignore_folders.map { |path| "--exclude #{path}" }.join(' ')
68
+ def with_temp_rsync_ignores
69
+ Entangler::Helper.with_temp_file(name: 'rsync_ignores', contents: find_rsync_ignore_folders.join("\n")) do |f|
70
+ yield f.path
71
+ end
72
+ end
73
+
74
+ def rsync_cmd_string(rsync_ignores_file_path)
112
75
  remote_path = @opts[:remote_mode] ? "#{@opts[:remote_user]}@#{@opts[:remote_host]}:" : ''
113
76
  remote_path += "#{@opts[:remote_base_dir]}/"
114
77
 
115
- cmd = "rsync -azv #{exclude_args}"
78
+ cmd = "rsync -azv --exclude-from #{rsync_ignores_file_path}"
116
79
  cmd += " -e \"ssh -p #{@opts[:remote_port]}\"" if @opts[:remote_mode]
117
80
  cmd + " --delete #{base_dir}/ #{remote_path}"
118
81
  end
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Entangler
2
4
  module Executor
3
5
  class Slave < Base
4
6
  def initialize(base_dir, opts = {})
5
7
  super(base_dir, opts)
6
- STDIN.binmode
7
- STDOUT.binmode
8
- STDIN.sync = true
9
- STDOUT.sync = true
8
+ $stdin.binmode
9
+ $stdout.binmode
10
+ $stdin.sync = true
11
+ $stdout.sync = true
10
12
 
11
- @remote_reader = STDIN
12
- @remote_writer = STDOUT
13
- $stderr.reopen(File.join(log_dir, 'entangler.err'), 'w')
13
+ @remote_reader = $stdin
14
+ @remote_writer = $stdout
15
+ $stderr.reopen(File.join(Entangler::Logger.log_file_path(base_dir, 'entangler.err')), 'w')
14
16
  end
15
17
  end
16
18
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Entangler
4
+ module Executor
5
+ module Validation
6
+ module Base
7
+ protected
8
+
9
+ def validate_opts; end
10
+
11
+ def validate_base_dir(base_dir)
12
+ raise Entangler::ValidationError, "Base directory doesn't exist" unless File.exist?(base_dir)
13
+ raise Entangler::ValidationError, 'Base directory is a file' unless File.directory?(base_dir)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Entangler
4
+ module Executor
5
+ module Validation
6
+ module Master
7
+ private
8
+
9
+ def validate_opts
10
+ super
11
+ if @opts[:remote_mode]
12
+ @opts[:remote_port] ||= '22'
13
+ validate_remote_opts
14
+ else
15
+ validate_local_opts
16
+ end
17
+ end
18
+
19
+ def validate_local_opts
20
+ unless File.exist?(@opts[:remote_base_dir])
21
+ raise Entangler::ValidationError, "Destination directory doesn't exist"
22
+ end
23
+ unless File.directory?(@opts[:remote_base_dir])
24
+ raise Entangler::ValidationError, 'Destination directory is a file'
25
+ end
26
+
27
+ @opts[:remote_base_dir] = File.realpath(File.expand_path(@opts[:remote_base_dir]))
28
+ return unless @opts[:remote_base_dir] == base_dir
29
+
30
+ raise Entangler::ValidationError, "Destination directory can't be the same as the base directory"
31
+ end
32
+
33
+ def validate_remote_opts
34
+ keys = @opts.keys
35
+ raise Entangler::ValidationError, 'Missing remote base dir' unless keys.include?(:remote_base_dir)
36
+ raise Entangler::ValidationError, 'Missing remote user' unless keys.include?(:remote_user)
37
+ raise Entangler::ValidationError, 'Missing remote host' unless keys.include?(:remote_host)
38
+
39
+ validate_remote_base_dir
40
+ validate_remote_entangler_version
41
+ end
42
+
43
+ def validate_remote_base_dir
44
+ res = `#{generate_ssh_command("[[ -d '#{@opts[:remote_base_dir]}' ]] && echo 'ok' || echo 'missing'")}`
45
+ raise Entangler::ValidationError, 'Cannot connect to remote' if res.empty?
46
+ raise Entangler::ValidationError, 'Remote base dir invalid' unless res.strip == 'ok'
47
+ end
48
+
49
+ def validate_remote_entangler_version
50
+ return unless @opts[:remote_mode]
51
+
52
+ res = `#{generate_ssh_command('source ~/.rvm/environments/default && entangler --version')}`
53
+ remote_version = Gem::Version.new(res.strip)
54
+ local_version = Gem::Version.new(Entangler::VERSION)
55
+ return unless major_version_mismatch?(local_version, remote_version)
56
+
57
+ msg = 'Entangler version too far apart, please update either local or remote Entangler.' \
58
+ " Local version is #{local_version} and remote version is #{remote_version}."
59
+ raise Entangler::VersionMismatchError, msg
60
+ end
61
+
62
+ def major_version_mismatch?(version1, version2)
63
+ version1.segments[0] != version2.segments[0] ||
64
+ (version1.segments[0].zero? && version1 != version2) ||
65
+ ((version1.prerelease? || version2.prerelease?) && version1 != version2)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'logger'
5
+
6
+ module Entangler
7
+ class Logger
8
+ def self.create_log_dir(base_dir)
9
+ FileUtils.mkdir_p(File.dirname(log_file_path(base_dir)))
10
+ end
11
+
12
+ def self.log_file_path(base_dir, log_file_name = 'entangler.log')
13
+ File.join(base_dir, '.entangler', 'log', log_file_name)
14
+ end
15
+
16
+ def initialize(outputs, verbose: false)
17
+ @loggers = Array(outputs).map do |output|
18
+ logger = ::Logger.new(output)
19
+
20
+ logger.level = verbose ? ::Logger::DEBUG : ::Logger::INFO
21
+ logger.formatter = proc do |severity, datetime, _, msg|
22
+ date_format = datetime.strftime('%Y-%m-%d %H:%M:%S')
23
+ "[#{date_format}] #{severity.rjust(5)}: #{msg}\n"
24
+ end
25
+
26
+ logger
27
+ end
28
+ end
29
+
30
+ def level=(level)
31
+ @loggers.each { |logger| logger.level = level }
32
+ end
33
+
34
+ ::Logger::Severity.constants.each do |level|
35
+ define_method(level.downcase) do |*args|
36
+ @loggers.each { |logger| logger.send(level.downcase, *args) }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Entangler
2
- VERSION = '1.0.1'.freeze
4
+ VERSION = '1.2.0'
3
5
  end
metadata CHANGED
@@ -1,99 +1,127 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: entangler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Allie
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-12-21 00:00:00.000000000 Z
11
+ date: 2020-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.12'
19
+ version: '2.1'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.12'
26
+ version: '2.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 13.0.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 13.0.1
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.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '3.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - "~>"
32
60
  - !ruby/object:Gem::Version
33
- version: '10.0'
61
+ version: '1.6'
34
62
  type: :development
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
- version: '10.0'
68
+ version: '1.6'
41
69
  - !ruby/object:Gem::Dependency
42
- name: rspec
70
+ name: rubocop-rake
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: '3.0'
75
+ version: 0.5.1
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '3.0'
82
+ version: 0.5.1
55
83
  - !ruby/object:Gem::Dependency
56
- name: rubocop
84
+ name: rubocop-rspec
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
87
  - - "~>"
60
88
  - !ruby/object:Gem::Version
61
- version: '0.46'
89
+ version: '2.0'
62
90
  type: :development
63
91
  prerelease: false
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
94
  - - "~>"
67
95
  - !ruby/object:Gem::Version
68
- version: '0.46'
96
+ version: '2.0'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: listen
71
99
  requirement: !ruby/object:Gem::Requirement
72
100
  requirements:
73
101
  - - "~>"
74
102
  - !ruby/object:Gem::Version
75
- version: '3.1'
103
+ version: '3.3'
76
104
  type: :runtime
77
105
  prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
- version: '3.1'
110
+ version: '3.3'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: to_regexp
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
115
  - - "~>"
88
116
  - !ruby/object:Gem::Version
89
- version: '0.2'
117
+ version: 0.2.1
90
118
  type: :runtime
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
122
  - - "~>"
95
123
  - !ruby/object:Gem::Version
96
- version: '0.2'
124
+ version: 0.2.1
97
125
  description: Two way file syncer using platform native notify.
98
126
  email:
99
127
  - dave@daveallie.com
@@ -102,17 +130,10 @@ executables:
102
130
  extensions: []
103
131
  extra_rdoc_files: []
104
132
  files:
105
- - ".gitignore"
106
- - ".rspec"
107
- - ".rubocop.yml"
108
- - ".travis.yml"
109
133
  - CODE_OF_CONDUCT.md
110
134
  - Gemfile
111
135
  - LICENSE.txt
112
136
  - README.md
113
- - Rakefile
114
- - bin/console
115
- - bin/setup
116
137
  - entangler.gemspec
117
138
  - exe/entangler
118
139
  - lib/entangler.rb
@@ -121,8 +142,12 @@ files:
121
142
  - lib/entangler/executor/background/base.rb
122
143
  - lib/entangler/executor/background/master.rb
123
144
  - lib/entangler/executor/base.rb
145
+ - lib/entangler/executor/helpers.rb
124
146
  - lib/entangler/executor/master.rb
125
147
  - lib/entangler/executor/slave.rb
148
+ - lib/entangler/executor/validation/base.rb
149
+ - lib/entangler/executor/validation/master.rb
150
+ - lib/entangler/logger.rb
126
151
  - lib/entangler/version.rb
127
152
  homepage: https://github.com/daveallie/entangler
128
153
  licenses:
@@ -136,15 +161,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
136
161
  requirements:
137
162
  - - ">="
138
163
  - !ruby/object:Gem::Version
139
- version: '0'
164
+ version: '2.5'
140
165
  required_rubygems_version: !ruby/object:Gem::Requirement
141
166
  requirements:
142
167
  - - ">="
143
168
  - !ruby/object:Gem::Version
144
169
  version: '0'
145
170
  requirements: []
146
- rubyforge_project:
147
- rubygems_version: 2.4.8
171
+ rubygems_version: 3.1.2
148
172
  signing_key:
149
173
  specification_version: 4
150
174
  summary: Two way file syncer using platform native notify.
data/.gitignore DELETED
@@ -1,11 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- /log/
11
- *.gem
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color
@@ -1,9 +0,0 @@
1
- AllCops:
2
- DisplayCopNames: true
3
- ExtraDetails: true
4
-
5
- Metrics/LineLength:
6
- Max: 120
7
-
8
- Style/Documentation:
9
- Enabled: false
@@ -1,20 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- os:
4
- - linux
5
- - osx
6
- rvm:
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/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- require 'bundler/gem_tasks'
2
- require 'rspec/core/rake_task'
3
- require 'rubocop/rake_task'
4
-
5
- RuboCop::RakeTask.new(:rubocop)
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task default: [:rubocop, :spec]
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'bundler/setup'
4
- require 'entangler'
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require 'irb'
14
- IRB.start
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here