warp-dir 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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?