mpx 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 39087fb1774c7fd9ec0f3ca2d33d703d851820c9b832dd5602c46b80112a7487
4
+ data.tar.gz: 8d7186b216e9946eb648bfae8ea4cf167a44559b5d5bdca1c227487cf654070a
5
+ SHA512:
6
+ metadata.gz: aff6db3225fe63108dd54ee6ff913ae604bf0b9a6efe60311077c9041a5b333f8d82411111151aa807a953568dbb53f4bd7c81c904f7c74f58f49621982d7c10
7
+ data.tar.gz: ddf9a1cde5a057010bb2610dd0ddfadf240650ca700f742bbbdc506334c92e33d0decd891a54951ffedf192cc52e5db6f26dcd4b79fe6461e770ed8356abf4c0
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.0
7
+ before_install: gem install bundler -v 2.0.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mpx.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mpx (0.1.0)
5
+ ansi (~> 1.5)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ansi (1.5.0)
11
+ builder (3.2.3)
12
+ minitest (5.11.3)
13
+ minitest-reporters (1.3.6)
14
+ ansi
15
+ builder
16
+ minitest (>= 5.0)
17
+ ruby-progressbar
18
+ rake (10.5.0)
19
+ ruby-progressbar (1.10.0)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ bundler (~> 2.0)
26
+ minitest (~> 5.0)
27
+ minitest-reporters (~> 1.3)
28
+ mpx!
29
+ rake (~> 10.0)
30
+
31
+ BUNDLED WITH
32
+ 2.0.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Ethan Chan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # mpx
2
+
3
+ ```
4
+ A command multiplexer.
5
+
6
+ The root folder MPX_ROOT is an environment variable
7
+ which defaults to `~/.local/mpx`.
8
+
9
+ The following subfolders are used:
10
+ - `bin` Where commands are stored.
11
+ - `spaces` Namespaces for each command.
12
+ Each command receives a subfolder with its name in `spaces`.
13
+ The working directory will be changed to this subfolder before
14
+ command execution.
15
+ - `sets` Aliases to sets of commands. Each file is a set,
16
+ containing newline-delimited commands to run.
17
+ - `history` Newline-delimited history of each command/alias, with timestamp.
18
+
19
+ The first argument is a mandatory operation, and should be one of:
20
+
21
+ `help`
22
+
23
+ Prints this help text.
24
+
25
+ `version`
26
+
27
+ Prints the version.
28
+
29
+ `history`
30
+
31
+ Taking each subsequent argument as a command or set, the history of each
32
+ will be displayed in chronological order.
33
+
34
+ If no arguments are provided, the history of all commands and sets
35
+ will be displayed.
36
+
37
+ A directive in the form of `<COMMAND/SET>:<SUBCOMMAND>` or `:<SUBCOMMAND>`
38
+
39
+ In the first form, `<COMMAND/SET>` will be taken as the command or set
40
+ to run with. If names clash, commands will take precedence over sets.
41
+ For a given set, the program will run all commands in the set file.
42
+
43
+ In the second form, the program will run with all commands.
44
+
45
+ In both forms, `<SUBCOMMAND>` will be passed as the first argument
46
+ to each command. All arguments after the directive will be passed directly.
47
+
48
+ If multiple commands run, they shall run in parallel,
49
+ and outputs will be displayed upon completion.
50
+ ```
51
+
52
+ ## Installation
53
+
54
+ Add this line to your application's Gemfile:
55
+
56
+ ```ruby
57
+ gem 'mpx'
58
+ ```
59
+
60
+ And then execute:
61
+
62
+ $ bundle
63
+
64
+ Or install it yourself as:
65
+
66
+ $ gem install mpx
67
+
68
+ ## Development
69
+
70
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
71
+
72
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
73
+
74
+ ## Contributing
75
+
76
+ Bug reports and pull requests are welcome on GitHub at https://github.com/metakirby5/mpx.
77
+
78
+ ## License
79
+
80
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/setup ADDED
@@ -0,0 +1,8 @@
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
data/exe/mpx ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "mpx"
data/lib/mpx/cli.rb ADDED
@@ -0,0 +1,132 @@
1
+ require 'mpx/request'
2
+ require 'mpx/loader'
3
+
4
+ module Mpx
5
+
6
+ Help = <<-EOF
7
+ A command multiplexer.
8
+
9
+ The root folder MPX_ROOT is an environment variable
10
+ which defaults to `~/.local/mpx`.
11
+
12
+ The following subfolders are used:
13
+ - `bin` Where commands are stored.
14
+ - `spaces` Namespaces for each command.
15
+ Each command receives a subfolder with its name in `spaces`.
16
+ The working directory will be changed to this subfolder before
17
+ command execution.
18
+ - `sets` Aliases to sets of commands. Each file is a set,
19
+ containing newline-delimited commands to run.
20
+ - `history` Newline-delimited history of each command/alias, with timestamp.
21
+
22
+ The first argument is a mandatory operation, and should be one of:
23
+
24
+ `help`
25
+
26
+ Prints this help text.
27
+
28
+ `version`
29
+
30
+ Prints the version.
31
+
32
+ `history`
33
+
34
+ Taking each subsequent argument as a command or set, the history of each
35
+ will be displayed in chronological order.
36
+
37
+ If no arguments are provided, the history of all commands and sets
38
+ will be displayed.
39
+
40
+ A directive in the form of `<COMMAND/SET>:<SUBCOMMAND>` or `:<SUBCOMMAND>`
41
+
42
+ In the first form, `<COMMAND/SET>` will be taken as the command or set
43
+ to run with. If names clash, commands will take precedence over sets.
44
+ For a given set, the program will run all commands in the set file.
45
+
46
+ In the second form, the program will run with all commands.
47
+
48
+ In both forms, `<SUBCOMMAND>` will be passed as the first argument
49
+ to each command. All arguments after the directive will be passed directly.
50
+
51
+ If multiple commands run, they shall run in parallel,
52
+ and outputs will be displayed upon completion.
53
+ EOF
54
+
55
+ ##
56
+ # Command line interface.
57
+ class Cli
58
+ Usage = "Usage: #{File.basename($0)} [operation] [args...]"
59
+ MpxRoot = 'MPX_ROOT'
60
+ DefaultRoot = File.join('.local', 'mpx')
61
+
62
+ def self.start
63
+ op, *args = ARGV
64
+ case op
65
+ when 'help'
66
+ self.help
67
+ when 'version'
68
+ self.version
69
+ when 'history'
70
+ self.history(args)
71
+ when nil
72
+ self.help
73
+ exit 1
74
+ else
75
+ self.run(op, *args)
76
+ end
77
+ rescue => e
78
+ puts "Error: #{e}."
79
+ puts Usage
80
+ exit 1
81
+ end
82
+
83
+ def self.help
84
+ puts Help
85
+ puts
86
+ puts Usage
87
+ end
88
+
89
+ def self.version
90
+ puts Mpx::VERSION
91
+ end
92
+
93
+ def self.history(args)
94
+ history = Loader.new(self.root).history
95
+ puts history.get(*args)
96
+ end
97
+
98
+ def self.run(args)
99
+ request = Request.new(args)
100
+ loader = Loader.new(self.root)
101
+ commands = loader.load(request.name).sort
102
+ history = loader.history
103
+
104
+ threads = commands.map do |command|
105
+ Thread.new do
106
+ result = command.run(request.args)
107
+ history.write(command.name, *request.args)
108
+ result
109
+ end
110
+ end
111
+
112
+ threads.each do |t|
113
+ puts t.value
114
+ puts
115
+ end
116
+ end
117
+
118
+ def self.root
119
+ root = ENV[MpxRoot]
120
+ if root
121
+ return root
122
+ end
123
+
124
+ home = ENV['HOME']
125
+ if !home
126
+ raise "#{MpxRoot} and $HOME are both not set"
127
+ end
128
+
129
+ return File.join(home, DefaultRoot)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,28 @@
1
+ require 'open3'
2
+ require 'fileutils'
3
+
4
+ require 'mpx/result'
5
+
6
+ module Mpx
7
+ ##
8
+ # Represents a command to be run.
9
+ class Command
10
+ def initialize(bin, working_directory)
11
+ @bin = bin
12
+ @working_directory = working_directory
13
+ end
14
+
15
+ def <=>(other)
16
+ return self.name <=> other.name
17
+ end
18
+
19
+ def name
20
+ return File.basename(@bin)
21
+ end
22
+
23
+ def run(args)
24
+ opened = Open3.capture2e(@bin, *args, :chdir => @working_directory)
25
+ return Result.new(name, *opened)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ require 'date'
2
+
3
+ module Mpx
4
+ ##
5
+ # Manages history for commands.
6
+ class History
7
+ def initialize(root)
8
+ @root = root
9
+
10
+ FileUtils.mkdir_p(@root)
11
+ end
12
+
13
+ def now
14
+ return DateTime.now.strftime("%d/%m/%Y %H:%M")
15
+ end
16
+
17
+ def all_history
18
+ return Dir.entries(@root)
19
+ .select { |f| File.file?(File.join(@root, f)) }
20
+ end
21
+
22
+ def get(*commands)
23
+ return (commands.empty? ? all_history : commands)
24
+ .map { |c| history_for(c) }
25
+ .flatten
26
+ .sort_by do |line|
27
+ time, * = line.split('$')
28
+ DateTime.parse(time.strip)
29
+ end
30
+ end
31
+
32
+ def history_for(command)
33
+ File.foreach(File.join(@root, command))
34
+ .map { |line| line.strip }
35
+ rescue
36
+ raise "no history for #{command}"
37
+ end
38
+
39
+ def write(command, *args)
40
+ File.open(File.join(@root, command), 'a') do |f|
41
+ f.puts("#{now} $ #{command} #{args.join(' ')}")
42
+ end
43
+ end
44
+ end
45
+ end
data/lib/mpx/loader.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'mpx/command'
2
+ require 'mpx/history'
3
+
4
+ module Mpx
5
+ ##
6
+ # Responsible for path manipulation.
7
+ class Loader
8
+ BinFolder = 'bin'
9
+ SpacesFolder = 'spaces'
10
+ SetsFolder = 'sets'
11
+ HistoryFolder = 'history'
12
+
13
+ def initialize(root)
14
+ @bin = File.join(root, BinFolder)
15
+ @spaces = File.join(root, SpacesFolder)
16
+ @sets = File.join(root, SetsFolder)
17
+ @history = History.new(File.join(root, HistoryFolder))
18
+ end
19
+
20
+ def load(name)
21
+ if name.nil?
22
+ return load_all
23
+ end
24
+
25
+ return [load_command(name)]
26
+ rescue
27
+ return load_set(name)
28
+ end
29
+
30
+ def load_all
31
+ return Dir.entries(@bin)
32
+ .select { |f| File.file?(File.join(@bin, f)) }
33
+ .map { |file| load_command(file) }
34
+ end
35
+
36
+ def load_command(command)
37
+ bin_path = File.join(@bin, command)
38
+ if !File.exist?(bin_path)
39
+ raise "no command found with name `#{command}`"
40
+ end
41
+
42
+ space_path = File.join(@spaces, command)
43
+ FileUtils.mkdir_p(space_path)
44
+
45
+ return Command.new(bin_path, space_path)
46
+ end
47
+
48
+ def load_set(set)
49
+ set_path = File.join(@sets, set)
50
+ if !File.exist?(set_path)
51
+ raise "no command or set found with name `#{set}`"
52
+ end
53
+
54
+ return File.foreach(set_path)
55
+ .uniq
56
+ .map { |line| load_command(line.strip) }
57
+ end
58
+
59
+ def history
60
+ @history
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,28 @@
1
+ module Mpx
2
+ ##
3
+ # Extracts `<SUBCOMMAND/ALIAS>:<ARG> <ARGS>`.
4
+ class Request
5
+ def initialize(args)
6
+ directive, *rest = args
7
+ if !directive&.include? ':'
8
+ raise 'missing directive'
9
+ end
10
+
11
+ cmd, first_arg = directive.split ':', 2
12
+ if first_arg.empty?
13
+ raise 'missing first arg'
14
+ end
15
+
16
+ @name = cmd.empty? ? nil : cmd
17
+ @args = [first_arg, *rest]
18
+ end
19
+
20
+ def name
21
+ return @name
22
+ end
23
+
24
+ def args
25
+ return @args
26
+ end
27
+ end
28
+ end
data/lib/mpx/result.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'ansi/code'
2
+
3
+ module Mpx
4
+ ##
5
+ # Represents the output of a command.
6
+ class Result
7
+ include ANSI::Code
8
+
9
+ def initialize(name, out, status)
10
+ @name = name
11
+ @out = out
12
+ @status = status
13
+ end
14
+
15
+ def to_s
16
+ return [cyan { @name }, @out.strip, status_string].join("\n")
17
+ end
18
+
19
+ def status_string
20
+ color = @status.exitstatus.zero? ? :green : :red
21
+ return send(color) { @status }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Mpx
2
+ VERSION = "0.1.0"
3
+ end
data/lib/mpx.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "mpx/cli"
2
+
3
+ Mpx::Cli.start
data/mpx.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "mpx/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "mpx"
7
+ spec.version = Mpx::VERSION
8
+ spec.authors = ["Ethan Chan"]
9
+ spec.email = ["metakirby5@gmail.com"]
10
+
11
+ spec.summary = %q{A command multiplexer.}
12
+ spec.homepage = "https://github.com/metakirby5/mpx"
13
+ spec.license = "MIT"
14
+
15
+ if spec.respond_to?(:metadata)
16
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ else
20
+ raise "RubyGems 2.0 or newer is required to protect against " \
21
+ "public gem pushes."
22
+ end
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
27
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_dependency "ansi", "~> 1.5"
34
+
35
+ spec.add_development_dependency "bundler", "~> 2.0"
36
+ spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "minitest", "~> 5.0"
38
+ spec.add_development_dependency "minitest-reporters", "~> 1.3"
39
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mpx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ethan Chan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-01-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ansi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest-reporters
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
83
+ description:
84
+ email:
85
+ - metakirby5@gmail.com
86
+ executables:
87
+ - mpx
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/setup
99
+ - exe/mpx
100
+ - lib/mpx.rb
101
+ - lib/mpx/cli.rb
102
+ - lib/mpx/command.rb
103
+ - lib/mpx/history.rb
104
+ - lib/mpx/loader.rb
105
+ - lib/mpx/request.rb
106
+ - lib/mpx/result.rb
107
+ - lib/mpx/version.rb
108
+ - mpx.gemspec
109
+ homepage: https://github.com/metakirby5/mpx
110
+ licenses:
111
+ - MIT
112
+ metadata:
113
+ allowed_push_host: https://rubygems.org/
114
+ homepage_uri: https://github.com/metakirby5/mpx
115
+ source_code_uri: https://github.com/metakirby5/mpx
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubygems_version: 3.0.1
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: A command multiplexer.
135
+ test_files: []