warp-dir 1.1.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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.atom-build.json +22 -0
  3. data/.codeclimate.yml +22 -0
  4. data/.gitignore +40 -0
  5. data/.idea/encodings.xml +6 -0
  6. data/.idea/misc.xml +14 -0
  7. data/.idea/modules.xml +8 -0
  8. data/.idea/runConfigurations/All_Specs.xml +33 -0
  9. data/.idea/vcs.xml +6 -0
  10. data/.idea/warp-dir.iml +224 -0
  11. data/.rspec +4 -0
  12. data/.rubocop.yml +1156 -0
  13. data/.travis.yml +13 -0
  14. data/Gemfile +4 -0
  15. data/Guardfile +14 -0
  16. data/LICENSE +22 -0
  17. data/README.md +114 -0
  18. data/ROADMAP.md +96 -0
  19. data/Rakefile +24 -0
  20. data/bin/console +11 -0
  21. data/bin/setup +8 -0
  22. data/bin/warp-dir +13 -0
  23. data/bin/warp-dir.bash +25 -0
  24. data/lib/warp.rb +4 -0
  25. data/lib/warp/dir.rb +65 -0
  26. data/lib/warp/dir/app/cli.rb +162 -0
  27. data/lib/warp/dir/app/response.rb +133 -0
  28. data/lib/warp/dir/command.rb +120 -0
  29. data/lib/warp/dir/command/add.rb +16 -0
  30. data/lib/warp/dir/command/help.rb +80 -0
  31. data/lib/warp/dir/command/install.rb +78 -0
  32. data/lib/warp/dir/command/list.rb +13 -0
  33. data/lib/warp/dir/command/ls.rb +31 -0
  34. data/lib/warp/dir/command/remove.rb +16 -0
  35. data/lib/warp/dir/command/warp.rb +24 -0
  36. data/lib/warp/dir/commander.rb +71 -0
  37. data/lib/warp/dir/config.rb +87 -0
  38. data/lib/warp/dir/errors.rb +60 -0
  39. data/lib/warp/dir/formatter.rb +77 -0
  40. data/lib/warp/dir/point.rb +53 -0
  41. data/lib/warp/dir/serializer.rb +14 -0
  42. data/lib/warp/dir/serializer/base.rb +43 -0
  43. data/lib/warp/dir/serializer/dotfile.rb +36 -0
  44. data/lib/warp/dir/store.rb +129 -0
  45. data/lib/warp/dir/version.rb +6 -0
  46. data/spec/fixtures/warprc +2 -0
  47. data/spec/spec_helper.rb +71 -0
  48. data/spec/support/cli_expectations.rb +118 -0
  49. data/spec/warp/dir/app/cli_spec.rb +225 -0
  50. data/spec/warp/dir/app/response_spec.rb +131 -0
  51. data/spec/warp/dir/command_spec.rb +62 -0
  52. data/spec/warp/dir/commands/add_spec.rb +40 -0
  53. data/spec/warp/dir/commands/install_spec.rb +20 -0
  54. data/spec/warp/dir/commands/list_spec.rb +37 -0
  55. data/spec/warp/dir/config_spec.rb +45 -0
  56. data/spec/warp/dir/errors_spec.rb +16 -0
  57. data/spec/warp/dir/formatter_spec.rb +38 -0
  58. data/spec/warp/dir/point_spec.rb +35 -0
  59. data/spec/warp/dir/store_spec.rb +105 -0
  60. data/warp-dir.gemspec +56 -0
  61. metadata +228 -0
@@ -0,0 +1,31 @@
1
+ require 'warp/dir/command'
2
+ module Warp
3
+ module Dir
4
+ class Command
5
+ class LS < Warp::Dir::Command
6
+ description %q(List directory contents of a Warp Point)
7
+ needs_a_point? true
8
+ aliases :dir
9
+
10
+ # @param [Object] args
11
+ def run(opts, *flags)
12
+ point = store.find_point(point_name)
13
+ STDERR.puts "FLAGS: [#{flags}]".bold.green if config.debug
14
+
15
+ command_flags = if flags && !flags.empty?
16
+ flags
17
+ else
18
+ ['-al']
19
+ end
20
+ command = "ls #{command_flags.join(' ')} #{point.path}/"
21
+ STDERR.puts 'Command: '.yellow + command.bold.green if config.debug
22
+ ls_output = `#{command}`
23
+ STDERR.puts 'Output: '.yellow + ls_output.bold.blue if config.debug
24
+ on :success do
25
+ message ls_output.bold
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ require 'warp/dir/command'
2
+ class Warp::Dir::Command
3
+ class Remove < Warp::Dir::Command
4
+ description %q(Removes a given warp point from the database)
5
+ needs_a_point? true
6
+ aliases :rm, :delete
7
+
8
+ def run(*args)
9
+ point_name = self.point_name
10
+ store.remove point_name: point_name
11
+ on :success do
12
+ message "Warp point #{point_name.to_s.yellow} has been removed."
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ require 'warp/dir/command'
2
+
3
+ require 'colored'
4
+ module Warp
5
+ module Dir
6
+ class Command
7
+ class Warp < Warp::Dir::Command
8
+ description %q(Jumps to the pre-defined warp point (command optional))
9
+ needs_a_point? true
10
+
11
+ def run(*args)
12
+ if point.nil? && point_name
13
+ self.point = store[point_name]
14
+ end
15
+ raise ::Warp::Dir::Errors::PointNotFound.new(point) unless point
16
+ p = point
17
+ on :shell do
18
+ message "cd #{p.absolute_path}"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ require 'set'
2
+ require 'warp/dir/errors'
3
+ require 'warp/dir/formatter'
4
+ require 'singleton'
5
+ module Warp
6
+ module Dir
7
+ class Commander
8
+ include Singleton
9
+
10
+ attr_reader :commands
11
+ attr_accessor :command_map
12
+
13
+ def initialize
14
+ @commands ||= Set.new # a pre-caution, normally it would already by defined by now
15
+ @command_map = {}
16
+ end
17
+
18
+ def register(command)
19
+ @commands << command if command
20
+ self
21
+ end
22
+
23
+ def installed_commands
24
+ @commands.map(&:command_name)
25
+ end
26
+
27
+ def lookup(command_name)
28
+ reindex!
29
+ command_map[command_name]
30
+ end
31
+
32
+ def find(command_name)
33
+ command = lookup(command_name)
34
+ if command.nil?
35
+ raise ::Warp::Dir::Errors::InvalidCommand.new(command_name)
36
+ end
37
+ command
38
+ end
39
+
40
+ def run(command_name, *args)
41
+ cmd = find command_name
42
+ raise ::Warp::Dir::Errors::InvalidCommand.new(command_name) unless cmd.is_a?(warp::Dir::Command)
43
+ cmd.new(*args).run
44
+ end
45
+
46
+ def reindex!
47
+ commands.each do |command|
48
+ if command.respond_to?(:aliases)
49
+ command.aliases.each do |an_alias|
50
+ if self.command_map[an_alias] && !self.command_map[an_alias] == command
51
+ raise Warp::Dir::Errors::InvalidCommand.new("Duplicate alias for command #{command}")
52
+ end
53
+ self.command_map[an_alias] = command
54
+ end
55
+ end
56
+ self.command_map[command.command_name] = command
57
+ end
58
+ self
59
+ end
60
+
61
+ def validate!
62
+ self.commands.delete_if do |subclass|
63
+ if !subclass.respond_to?(:abstract_class?) && !subclass.method_defined?(:run)
64
+ raise ::Warp::Dir::Errors::InvalidCommand.new(subclass)
65
+ end
66
+ subclass.respond_to?(:abstract_class?) || !subclass.method_defined?(:run)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,87 @@
1
+ require 'forwardable'
2
+
3
+ module Warp
4
+ module Dir
5
+ class Config
6
+
7
+ DEFAULTS = {
8
+ warprc: ENV['HOME'] + '/.warprc',
9
+ shell: false,
10
+ force: false,
11
+ debug: false
12
+ }
13
+
14
+ attr_accessor :variables
15
+
16
+ extend ::Forwardable
17
+ def_delegators :@variables, :size, :<<, :map, :each
18
+ def initialize(opts = {})
19
+ configure(opts)
20
+ end
21
+
22
+ def configure(opts = {})
23
+ options = DEFAULTS.merge(opts)
24
+
25
+ # Move :config hash key->value to :warprc that is expected by the Config
26
+ if options[:config]
27
+ options[:warprc] = options[:config]
28
+ options.delete(:config)
29
+ end
30
+
31
+ self.variables = []
32
+
33
+ # OK. I concede. This is very likely a total overkill :O
34
+ # The thing is: I just really like calling hash members via
35
+ # methods, and I didn't want to load HashieMash because
36
+ # it's big. Real big.
37
+ #
38
+ # IRB Session that explains it all:
39
+ #
40
+ # c = Config.new({ foo: "bar", bar: "foo"})
41
+ # => #<Warp::Dir::Config:0x007ff8e39531f0 @variables=[:config, :foo, :bar], @config="/Users/kigster/.warprc", @foo="bar", @bar="foo">
42
+ # > c.foo # => "bar"
43
+ # > c.bar # => "foo"
44
+ # > c.bar? # => true
45
+ # > c.config # => "/Users/kigster/.warprc"
46
+ # > c.color = "red" # => "red"
47
+ # > c.color # => "red"
48
+ # > c.color? # => true
49
+
50
+ options.each_pair do |variable_name, value|
51
+ self.variables << add_config_variable(variable_name, value)
52
+ end
53
+ end
54
+
55
+ # allow syntax @config[:warprc]
56
+ def [](key)
57
+ self.send(key)
58
+ end
59
+
60
+ # Dispatches redis operations to master/slaves.
61
+ def method_missing(method, *args, &block)
62
+ if method =~ /=$/
63
+ add_config_variable(method.to_s.gsub(/=$/, ''), *args)
64
+ else
65
+ super
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def add_config_variable(variable_name, value)
72
+ reader = variable_name.to_sym
73
+ writer = "#{reader}=".to_sym
74
+ boolean = "#{reader}?"
75
+ variable = "@#{reader}".to_sym
76
+ # set this value on the the instance of config class
77
+ instance_variable_set(variable, value)
78
+ # add the reader and the writer to this key
79
+ define_singleton_method(reader) { instance_variable_get(variable) }
80
+ define_singleton_method(writer) { |new_value| instance_variable_set(variable, new_value) }
81
+ define_singleton_method(boolean){ !instance_variable_get(variable).nil? }
82
+ reader
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,60 @@
1
+ module Warp
2
+ module Dir
3
+ module Errors
4
+ class Runtime < RuntimeError;
5
+ end
6
+
7
+ class StoreFormatError < Warp::Dir::Errors::Runtime
8
+ attr_reader :line
9
+ def initialize(msg, line)
10
+ @line = line
11
+ super msg
12
+ end
13
+ end
14
+
15
+ class StoreUninitialized < Warp::Dir::Errors::Runtime; end
16
+ class StoreAlreadyInitialized < Warp::Dir::Errors::Runtime; end
17
+
18
+ # This is a generic Exception that wraps an object passed to the
19
+ # initializer and assumed to be the reason for the failure.
20
+ # Message is optional, but each concrete exception should provide
21
+ # it's own concrete message
22
+ class InstanceError < Warp::Dir::Errors::Runtime
23
+ attr_accessor :instance
24
+ def initialize(message = nil)
25
+ super message ? message : "#{self.class.name}->[#{instance}]"
26
+ end
27
+
28
+ def name
29
+ super.gsub(%r{#{self.class.name}}, '')
30
+ end
31
+
32
+ def color_error instance_type, instance, result
33
+ instance_type.red.bold +
34
+ instance.to_s.yellow.bold +
35
+ result.red.bold
36
+ end
37
+ end
38
+
39
+ class InvalidCommand < ::Warp::Dir::Errors::InstanceError
40
+ def initialize(instance = nil)
41
+ self.instance = instance
42
+ super instance.is_a?(Symbol) ? color_error('Command ', instance, ' is invalid.') : instance
43
+ end
44
+ end
45
+
46
+ class PointNotFound < ::Warp::Dir::Errors::InstanceError
47
+ def initialize(point)
48
+ self.instance = point
49
+ super color_error('Point ', point.to_s, ' was not found.')
50
+ end
51
+ end
52
+ class PointAlreadyExists < ::Warp::Dir::Errors::InstanceError
53
+ def initialize(point)
54
+ self.instance = point
55
+ super color_error('Point ', point.to_s, ' already exists. Pass --force to overwrite.')
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,77 @@
1
+ require 'warp/dir'
2
+ require 'warp/dir/errors'
3
+ require 'colored'
4
+ require 'thread'
5
+ module Warp
6
+ module Dir
7
+ class Formatter
8
+ DEFAULT_FORMAT = :ascii
9
+
10
+ attr_accessor :store
11
+
12
+ def initialize(store)
13
+ @store = store
14
+ @config = store.config
15
+ end
16
+
17
+ def unhappy(exception: nil, message: nil)
18
+ out = 'Whoops! – '.white
19
+ out << "#{exception.message} ".red if exception && !message
20
+ out << "#{message} ".red if !exception && message
21
+ out << "#{exception.message}:\n#{message}".red if message && exception
22
+ out << "\n"
23
+ print ? STDERR.printf(out) : out
24
+ end
25
+
26
+ def format_point(point, *args)
27
+ PointFormatter.new(point).format(*args)
28
+ end
29
+
30
+ def format_store(*args)
31
+ StoreFormatter.new(store).format(*args)
32
+ end
33
+
34
+ def happy(message: nil)
35
+ STDOUT.printf(message.blue.bold)
36
+ end
37
+
38
+ private
39
+
40
+ class PointFormatter
41
+ attr_accessor :point
42
+
43
+ def initialize(point)
44
+ @point = point
45
+ end
46
+
47
+ def format(type = DEFAULT_FORMAT, width = 0)
48
+ case type
49
+ when :ascii
50
+ point.to_s(width)
51
+ else
52
+ raise ArgumentError.new("Type #{type} is not recognized.")
53
+ end
54
+ end
55
+ end
56
+
57
+ class StoreFormatter
58
+ attr_accessor :store
59
+
60
+ def initialize(store)
61
+ @store = store
62
+ end
63
+
64
+ # find the widest warp point name, and indent them all based on that.
65
+ # make it easy to extend to other types, and allow the caller to
66
+ # sort by one of the fields.
67
+ def format(type = DEFAULT_FORMAT, sort_field = :name)
68
+ longest_key_length = store.points.map(&:name).map(&:length).sort.last
69
+ Warp::Dir.sort_by(store.points, sort_field).map do |point|
70
+ PointFormatter.new(point).format(type, longest_key_length)
71
+ end.join("\n")
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
@@ -0,0 +1,53 @@
1
+ require 'forwardable'
2
+ require 'digest'
3
+ module Warp
4
+ module Dir
5
+ class Point
6
+ attr_accessor :full_path, :name
7
+
8
+ def initialize(name, full_path)
9
+ raise ArgumentError.new ':name is required' if name.nil?
10
+ raise ArgumentError.new ':full_path is required' if full_path.nil?
11
+ @full_path = Warp::Dir.absolute full_path
12
+ @name = name.to_sym
13
+ end
14
+
15
+ def absolute_path
16
+ full_path
17
+ end
18
+
19
+ def relative_path
20
+ Warp::Dir.relative full_path
21
+ end
22
+
23
+ def path
24
+ absolute_path
25
+ end
26
+
27
+ def inspect
28
+ sprintf("(#{object_id})[name: '%s', path: '%s']", name, relative_path)
29
+ end
30
+
31
+ def to_s(width = 0)
32
+ sprintf("%#{width}s -> %s", name, relative_path)
33
+ end
34
+
35
+ def hash
36
+ Digest::SHA1.base64digest("#{full_path.hash}#{name.hash}").hash
37
+ end
38
+
39
+ def <=>(other)
40
+ name <=> other.name
41
+ end
42
+
43
+ def eql?(another)
44
+ return false unless another.is_a?(Warp::Dir::Point)
45
+ %i(name full_path).each do |attribute|
46
+ return false unless send(attribute) == another.send(attribute)
47
+ end
48
+ true
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ module Warp
2
+ module Dir
3
+ SERIALIZERS = {}
4
+ module Serializer
5
+ def self.default
6
+ SERIALIZERS.values.first
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ Warp::Dir.require_all_from '/dir/serializer'
13
+
14
+ raise 'No concrete serializer implementations were found' if Warp::Dir::SERIALIZERS.empty?