warp-dir 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.atom-build.json +22 -0
- data/.codeclimate.yml +22 -0
- data/.gitignore +40 -0
- data/.idea/encodings.xml +6 -0
- data/.idea/misc.xml +14 -0
- data/.idea/modules.xml +8 -0
- data/.idea/runConfigurations/All_Specs.xml +33 -0
- data/.idea/vcs.xml +6 -0
- data/.idea/warp-dir.iml +224 -0
- data/.rspec +4 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +13 -0
- data/Gemfile +4 -0
- data/Guardfile +14 -0
- data/LICENSE +22 -0
- data/README.md +114 -0
- data/ROADMAP.md +96 -0
- data/Rakefile +24 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/bin/warp-dir +13 -0
- data/bin/warp-dir.bash +25 -0
- data/lib/warp.rb +4 -0
- data/lib/warp/dir.rb +65 -0
- data/lib/warp/dir/app/cli.rb +162 -0
- data/lib/warp/dir/app/response.rb +133 -0
- data/lib/warp/dir/command.rb +120 -0
- data/lib/warp/dir/command/add.rb +16 -0
- data/lib/warp/dir/command/help.rb +80 -0
- data/lib/warp/dir/command/install.rb +78 -0
- data/lib/warp/dir/command/list.rb +13 -0
- data/lib/warp/dir/command/ls.rb +31 -0
- data/lib/warp/dir/command/remove.rb +16 -0
- data/lib/warp/dir/command/warp.rb +24 -0
- data/lib/warp/dir/commander.rb +71 -0
- data/lib/warp/dir/config.rb +87 -0
- data/lib/warp/dir/errors.rb +60 -0
- data/lib/warp/dir/formatter.rb +77 -0
- data/lib/warp/dir/point.rb +53 -0
- data/lib/warp/dir/serializer.rb +14 -0
- data/lib/warp/dir/serializer/base.rb +43 -0
- data/lib/warp/dir/serializer/dotfile.rb +36 -0
- data/lib/warp/dir/store.rb +129 -0
- data/lib/warp/dir/version.rb +6 -0
- data/spec/fixtures/warprc +2 -0
- data/spec/spec_helper.rb +71 -0
- data/spec/support/cli_expectations.rb +118 -0
- data/spec/warp/dir/app/cli_spec.rb +225 -0
- data/spec/warp/dir/app/response_spec.rb +131 -0
- data/spec/warp/dir/command_spec.rb +62 -0
- data/spec/warp/dir/commands/add_spec.rb +40 -0
- data/spec/warp/dir/commands/install_spec.rb +20 -0
- data/spec/warp/dir/commands/list_spec.rb +37 -0
- data/spec/warp/dir/config_spec.rb +45 -0
- data/spec/warp/dir/errors_spec.rb +16 -0
- data/spec/warp/dir/formatter_spec.rb +38 -0
- data/spec/warp/dir/point_spec.rb +35 -0
- data/spec/warp/dir/store_spec.rb +105 -0
- data/warp-dir.gemspec +56 -0
- 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?
|