entangler 1.0.0 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 24ab1f0ac689c51c2a2b353c2cd9b9f6de09358e
4
- data.tar.gz: 61005288c87456ed526ee85b96c100a240315752
2
+ SHA256:
3
+ metadata.gz: 85849db77d8ac84bbb9e7230de8f2d20f88dba32786a029ae0fb06c2700707b6
4
+ data.tar.gz: 27e90f34ff9c31bc325b907926cf9906144f363d56b361100a62da2ab81a8539
5
5
  SHA512:
6
- metadata.gz: 6bff0242ca552222c8c2f8d9b6a85f4d6e5f7a4d2f1a05a830390513db30abd519867b907f383d62789e763c22a78bcf619e6c6b5eb836e252404e7cab5ea0bc
7
- data.tar.gz: 4e09843aa732a4f8263d86acac49767c35793a3060aea43886999e76466fdc4e8aa3e55974f095b1db599590643bf6116d2e66dd0f30bfe1287c61e38ffa13af
6
+ metadata.gz: c57b439657b3314fe8cc44018208e364610321de33939bc9cf34fceb4b0108de61c4385a9aaf7700748f122118af4d62a3fff267461eb912b8cf421103fc4ddd
7
+ data.tar.gz: 85da07278c48587669f37ed4e8e16abb4474ba55c3fc13a850dc322e1cea66f7934136261bc1302ade69074bbd6d29e9022609693a8db85d0edc48021c729117
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.0
22
+ Entangler v1.1.2
22
23
 
23
24
  Usage:
24
25
  entangler master <base_dir> <remote_user>@<remote_host>:<remote_base_dir> [options]
@@ -30,15 +31,16 @@ 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
- have to use regex. `-i '/^\.git(?:\/[^\/]+)+$/'` will match all sub-directories of `.git/`, but not the files in `.git`.
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
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.
44
46
 
@@ -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,16 @@ 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']
21
23
 
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'
24
+ spec.add_development_dependency 'bundler', '>= 2.1'
25
+ spec.add_development_dependency 'rake', '>= 12.3.3'
26
+ spec.add_development_dependency 'rspec', '>= 3.9'
27
+ spec.add_development_dependency 'rubocop', '>= 0.80'
26
28
  spec.add_dependency 'listen', '~> 3.1'
27
- spec.add_dependency 'to_regexp', '~> 0.2'
29
+ spec.add_dependency 'to_regexp', '~> 0.2.0'
28
30
  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,8 +111,9 @@ module Entangler
107
111
  @listener_pauses[idx] = true
108
112
  listener.pause
109
113
  yield
110
- @listener_pauses[idx] = false
111
- listener.start if @listener_pauses.none?
114
+ ensure
115
+ @listener_pauses[idx] = false
116
+ listener.start if @listener_pauses.none?
112
117
  end
113
118
 
114
119
  def with_log_time(msg)
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+
1
5
  module Entangler
2
6
  module Executor
3
7
  module Background
@@ -6,7 +10,6 @@ module Entangler
6
10
 
7
11
  def start_remote_slave
8
12
  logger.info('Starting - Entangler on remote')
9
- require 'open3'
10
13
  ignore_opts = @opts[:ignore].map { |regexp| "-i '#{regexp.inspect}'" }.join(' ')
11
14
  entangler_cmd = "entangler slave #{@opts[:remote_base_dir]} #{ignore_opts}"
12
15
  ssh_cmd = generate_ssh_command("source ~/.rvm/environments/default && #{entangler_cmd}")
@@ -20,7 +23,7 @@ module Entangler
20
23
  super
21
24
  begin
22
25
  Process.wait @remote_thread[:pid]
23
- rescue
26
+ rescue StandardError
24
27
  nil
25
28
  end
26
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)
@@ -35,6 +39,7 @@ module Entangler
35
39
  start_listener
36
40
  start_remote_io
37
41
  Signal.trap('INT') { kill_off_threads }
42
+ logger.info('Ready!')
38
43
  wait_for_threads
39
44
  ensure
40
45
  stop_listener
@@ -43,28 +48,16 @@ module Entangler
43
48
 
44
49
  protected
45
50
 
46
- def validate_opts; end
47
-
48
51
  def send_to_remote(msg = {})
49
52
  Marshal.dump(msg, @remote_writer)
50
53
  end
51
54
 
52
55
  def logger
53
- FileUtils.mkdir_p log_dir
54
- @logger ||= begin
55
- l = Logger.new(File.join(log_dir, 'entangler.log'))
56
- l.level = @opts[:verbose] ? Logger::DEBUG : Logger::INFO
57
- l
58
- end
59
- end
60
-
61
- def log_dir
62
- File.join(base_dir, '.entangler', 'log')
56
+ @logger ||= Entangler::Logger.new(log_outputs, @opts[:verbose])
63
57
  end
64
58
 
65
- def validate_base_dir(base_dir)
66
- raise Entangler::ValidationError, "Base directory doesn't exist" unless File.exist?(base_dir)
67
- 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)]
68
61
  end
69
62
  end
70
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Entangler
2
4
  module Executor
3
5
  class Slave < Base
@@ -10,7 +12,7 @@ module Entangler
10
12
 
11
13
  @remote_reader = STDIN
12
14
  @remote_writer = STDOUT
13
- $stderr.reopen(File.join(log_dir, 'entangler.err'), 'w')
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.0'.freeze
4
+ VERSION = '1.1.2'
3
5
  end
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: entangler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.2
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-10 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
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: 12.3.3
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: 12.3.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: '3.9'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: '3.9'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0.46'
61
+ version: '0.80'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0.46'
68
+ version: '0.80'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: listen
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.2'
89
+ version: 0.2.0
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.2'
96
+ version: 0.2.0
97
97
  description: Two way file syncer using platform native notify.
98
98
  email:
99
99
  - dave@daveallie.com
@@ -102,17 +102,10 @@ executables:
102
102
  extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
- - ".gitignore"
106
- - ".rspec"
107
- - ".rubocop.yml"
108
- - ".travis.yml"
109
105
  - CODE_OF_CONDUCT.md
110
106
  - Gemfile
111
107
  - LICENSE.txt
112
108
  - README.md
113
- - Rakefile
114
- - bin/console
115
- - bin/setup
116
109
  - entangler.gemspec
117
110
  - exe/entangler
118
111
  - lib/entangler.rb
@@ -121,8 +114,12 @@ files:
121
114
  - lib/entangler/executor/background/base.rb
122
115
  - lib/entangler/executor/background/master.rb
123
116
  - lib/entangler/executor/base.rb
117
+ - lib/entangler/executor/helpers.rb
124
118
  - lib/entangler/executor/master.rb
125
119
  - lib/entangler/executor/slave.rb
120
+ - lib/entangler/executor/validation/base.rb
121
+ - lib/entangler/executor/validation/master.rb
122
+ - lib/entangler/logger.rb
126
123
  - lib/entangler/version.rb
127
124
  homepage: https://github.com/daveallie/entangler
128
125
  licenses:
@@ -143,8 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
143
140
  - !ruby/object:Gem::Version
144
141
  version: '0'
145
142
  requirements: []
146
- rubyforge_project:
147
- rubygems_version: 2.4.8
143
+ rubygems_version: 3.1.2
148
144
  signing_key:
149
145
  specification_version: 4
150
146
  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