ready 0.0.1 → 0.0.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 +4 -4
- data/.rubocop.yml +57 -2
- data/README.md +76 -9
- data/Rakefile +91 -0
- data/demo/demofns.zsh +34 -0
- data/demo/rubyconf.rec +40 -0
- data/exe/ready +18 -1
- data/extra/browse.sh +161 -0
- data/extra/finder.sh +97 -0
- data/extra/formatter +108 -0
- data/extra/formatter.rb +92 -0
- data/extra/ri.rb +78 -0
- data/extra/rif +478 -0
- data/extra/rif2 +58 -0
- data/issues.rec +79 -0
- data/lib/ready/by_executable.rb +51 -0
- data/lib/ready/cli/clobber.rb +21 -0
- data/lib/ready/cli/compile.rb +111 -0
- data/lib/ready/cli/init.rb +30 -0
- data/lib/ready/cli/rake_command.rb +32 -0
- data/lib/ready/cli/up.rb +21 -0
- data/lib/ready/cli.rb +24 -0
- data/lib/ready/configuration.rb +60 -0
- data/lib/ready/executable/multiple_files_error.rb +3 -0
- data/lib/ready/executable.rb +94 -0
- data/lib/ready/fn.zsh.erb +28 -0
- data/lib/ready/readyfile/executable.rb +46 -0
- data/lib/ready/readyfile.rb +73 -0
- data/lib/ready/version.rb +1 -1
- data/lib/ready/zsh_function.rb +27 -0
- data/lib/ready/zsh_script.rb +45 -0
- data/lib/ready.rb +12 -0
- data/rakelib/ready.rake +191 -0
- data/readyfile +10 -0
- data/zsh/ready/functions/ready/__ready_datetime +32 -0
- data/zsh/ready/functions/ready/__ready_debug +51 -0
- data/zsh/ready/functions/ready/__ready_print +55 -0
- data/zsh/ready/functions/readyinit +78 -0
- data/zsh/ready/functions/readyup +64 -0
- data/zsh/ready/functions/rizz +50 -0
- data/zsh/ready/ready.plugin.zsh +49 -0
- data/zsh/ready.plugin.zsh +1 -0
- data/zsh/site-functions/_ready_ronin +12 -0
- metadata +67 -3
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
require "command_kit/command"
|
|
2
|
+
|
|
3
|
+
module Ready
|
|
4
|
+
class CLI
|
|
5
|
+
##
|
|
6
|
+
# Compiles ready stubs and prints them to stdout, or compiles everything via
|
|
7
|
+
# rake:
|
|
8
|
+
#
|
|
9
|
+
# * `ready compile` / `ready compile all`: compile every stub
|
|
10
|
+
# (`rake ready:compile`) -- bare `compile` defaults to `all`, like `make`
|
|
11
|
+
# * `ready compile by`: the persistent `by` client alias
|
|
12
|
+
# * `ready compile NAME ...`: a zsh function stub per CLI name
|
|
13
|
+
#
|
|
14
|
+
# The `--rubygems`/`--yjit` flags only apply to `by`; `--environment` only
|
|
15
|
+
# applies to named CLIs.
|
|
16
|
+
class Compile < CommandKit::Command
|
|
17
|
+
include RakeCommand
|
|
18
|
+
|
|
19
|
+
usage "[options] [all | by | NAME [NAME ...]]"
|
|
20
|
+
|
|
21
|
+
option :environment, short: "-e",
|
|
22
|
+
value: {
|
|
23
|
+
type: String,
|
|
24
|
+
usage: "KEY=VALUE",
|
|
25
|
+
},
|
|
26
|
+
desc: "Environment variable to set for the compiled CLI (repeatable)" do |pair|
|
|
27
|
+
key, value = pair.split("=", 2)
|
|
28
|
+
if value.nil? || key.empty?
|
|
29
|
+
print_error "invalid --environment #{pair.inspect}; expected KEY=VALUE"
|
|
30
|
+
exit(1)
|
|
31
|
+
end
|
|
32
|
+
@environment[key] = value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
option :rubygems, long: "--[no-]rubygems",
|
|
36
|
+
desc: "(by only) Load rubygems. Off by default for a big speed boost"
|
|
37
|
+
|
|
38
|
+
option :yjit, long: "--[no-]yjit",
|
|
39
|
+
desc: "(by only) Enable YJIT. Off by default"
|
|
40
|
+
|
|
41
|
+
argument :names, required: false,
|
|
42
|
+
repeats: true,
|
|
43
|
+
usage: "all | by | NAME",
|
|
44
|
+
desc: "`all` (the default when omitted), `by`, or one or more CLI names to compile"
|
|
45
|
+
|
|
46
|
+
description "Compile ready stubs for the given CLI name(s)"
|
|
47
|
+
|
|
48
|
+
examples [
|
|
49
|
+
"",
|
|
50
|
+
"all",
|
|
51
|
+
"by --yjit",
|
|
52
|
+
"irb rspec",
|
|
53
|
+
"-e RAILS_ENV=production rails",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
#
|
|
57
|
+
# Seeds the per-invocation environment accumulator that the `-e` option
|
|
58
|
+
# block writes into. The `--rubygems`/`--yjit` booleans are read straight
|
|
59
|
+
# from {#options}, which command_kit populates for us.
|
|
60
|
+
#
|
|
61
|
+
def initialize(**kwargs)
|
|
62
|
+
super
|
|
63
|
+
|
|
64
|
+
@environment = {}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
# Dispatches on the argument(s): `all` compiles everything via rake, `by`
|
|
69
|
+
# prints the persistent-client alias, and anything else is treated as one
|
|
70
|
+
# or more gem/CLI names.
|
|
71
|
+
#
|
|
72
|
+
# @param [Array<String>] names
|
|
73
|
+
#
|
|
74
|
+
def run(*names)
|
|
75
|
+
case names
|
|
76
|
+
in [] | ["all"] then rake("ready:compile")
|
|
77
|
+
in ["by"] then print by_alias
|
|
78
|
+
else
|
|
79
|
+
reject_reserved_names!(names)
|
|
80
|
+
print gem_script(names)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def by_alias
|
|
87
|
+
executable = ByExecutable.new
|
|
88
|
+
executable = options[:rubygems] ? executable.with_rubygems : executable.without_rubygems
|
|
89
|
+
executable = options[:yjit] ? executable.with_yjit : executable.without_yjit
|
|
90
|
+
executable.to_alias
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def gem_script(names)
|
|
94
|
+
ZshScript.new(names: names, environment: @environment).to_s
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
#
|
|
98
|
+
# `all` and `by` are whole-invocation modes, not gem names; reject them
|
|
99
|
+
# when they are mixed with other names rather than trying to compile a gem
|
|
100
|
+
# literally called "all" or "by".
|
|
101
|
+
#
|
|
102
|
+
def reject_reserved_names!(names)
|
|
103
|
+
reserved = names & %w[all by]
|
|
104
|
+
return if reserved.empty?
|
|
105
|
+
|
|
106
|
+
print_error "#{reserved.join(", ")} cannot be combined with other names"
|
|
107
|
+
exit(1)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "command_kit/command"
|
|
2
|
+
|
|
3
|
+
module Ready
|
|
4
|
+
class CLI
|
|
5
|
+
##
|
|
6
|
+
# Prints the shell snippet that sources ready's zsh plugin. The plugin path
|
|
7
|
+
# is resolved by introspection relative to this gem, so the output is
|
|
8
|
+
# correct wherever the gem is installed:
|
|
9
|
+
#
|
|
10
|
+
# eval "$(ready init)" # or paste the line into ~/.zshrc
|
|
11
|
+
class Init < CommandKit::Command
|
|
12
|
+
# The bundled zsh plugin entry point, resolved relative to the gem.
|
|
13
|
+
PLUGIN_PATH = Ready.root / "zsh" / "ready" / "ready.plugin.zsh"
|
|
14
|
+
|
|
15
|
+
description "Print the zsh snippet that sources the ready plugin"
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Prints `source <plugin path>` to stdout.
|
|
19
|
+
#
|
|
20
|
+
def run
|
|
21
|
+
unless PLUGIN_PATH.file?
|
|
22
|
+
print_error "ready zsh plugin not found at #{PLUGIN_PATH}"
|
|
23
|
+
exit(1)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
puts "source #{PLUGIN_PATH}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "English"
|
|
2
|
+
require "rbconfig"
|
|
3
|
+
|
|
4
|
+
module Ready
|
|
5
|
+
class CLI
|
|
6
|
+
##
|
|
7
|
+
# Shared behaviour for sub-commands that delegate to the project's Rakefile
|
|
8
|
+
# (`up`, `clobber`, and `compile all`).
|
|
9
|
+
#
|
|
10
|
+
# Rake is run under the same Ruby that is running `ready`, inheriting
|
|
11
|
+
# whatever Bundler context `exe/ready` established: in a development checkout
|
|
12
|
+
# a Gemfile is present, so `exe/ready` has already required `bundler/setup`
|
|
13
|
+
# (propagated to the rake process and the `ready compile` sub-processes it
|
|
14
|
+
# spawns via `RUBYOPT`); in production there is no Gemfile, so the build runs
|
|
15
|
+
# bundler-free and resolves the installed gem through RubyGems.
|
|
16
|
+
module RakeCommand
|
|
17
|
+
##
|
|
18
|
+
# Runs the given rake task(s) in {Ready.root} and exits non-zero if rake
|
|
19
|
+
# fails.
|
|
20
|
+
#
|
|
21
|
+
# @param [Array<String>] tasks
|
|
22
|
+
# The rake task name(s) to run.
|
|
23
|
+
def rake(*tasks)
|
|
24
|
+
return if system(RbConfig.ruby, "-S", "rake", *tasks, chdir: Ready.root.to_s)
|
|
25
|
+
|
|
26
|
+
# Propagate rake's own exit status where we can; fall back to 1 if the
|
|
27
|
+
# process could not be spawned at all ($? unset).
|
|
28
|
+
exit($CHILD_STATUS&.exitstatus || 1)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/ready/cli/up.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "command_kit/command"
|
|
2
|
+
|
|
3
|
+
module Ready
|
|
4
|
+
class CLI
|
|
5
|
+
##
|
|
6
|
+
# Compiles every stub and (re)starts the ready server, by delegating to the
|
|
7
|
+
# `rake ready` task.
|
|
8
|
+
class Up < CommandKit::Command
|
|
9
|
+
include RakeCommand
|
|
10
|
+
|
|
11
|
+
description "Compile all stubs and start the ready server"
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# Runs `rake ready`.
|
|
15
|
+
#
|
|
16
|
+
def run
|
|
17
|
+
rake("ready")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/ready/cli.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "command_kit/commands"
|
|
2
|
+
require "command_kit/options/version"
|
|
3
|
+
|
|
4
|
+
module Ready
|
|
5
|
+
##
|
|
6
|
+
# The `ready` command-line interface.
|
|
7
|
+
#
|
|
8
|
+
# `ready` speeds up Ruby CLI startup by preloading executables into a
|
|
9
|
+
# persistent server and compiling thin zsh stubs that dispatch to it. This
|
|
10
|
+
# class wires the sub-commands together with command_kit; each sub-command
|
|
11
|
+
# lives in its own file under `cli/`.
|
|
12
|
+
class CLI
|
|
13
|
+
include CommandKit::Commands
|
|
14
|
+
include CommandKit::Options::Version
|
|
15
|
+
|
|
16
|
+
command_name "ready"
|
|
17
|
+
version Ready::VERSION
|
|
18
|
+
|
|
19
|
+
command Init
|
|
20
|
+
command Up
|
|
21
|
+
command Compile
|
|
22
|
+
command Clobber
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Ready
|
|
2
|
+
##
|
|
3
|
+
# Resolves ready's runtime configuration (paths, prefixes, the readyfile)
|
|
4
|
+
# from the environment, falling back to conventional defaults.
|
|
5
|
+
class Configuration
|
|
6
|
+
def project_path
|
|
7
|
+
Pathname(__dir__).parent.parent.expand_path
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def prefix
|
|
11
|
+
fetched = fetch_env :prefix, default: "/tmp/ready"
|
|
12
|
+
|
|
13
|
+
Pathname(fetched).expand_path
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def build_dir
|
|
17
|
+
prefix / "builds"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def sock_path
|
|
21
|
+
fetched = fetch_env :sock_path do
|
|
22
|
+
prefix / "ready.sock"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Pathname(fetched)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def readyfile
|
|
29
|
+
@readyfile ||= open_readyfile
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def open_readyfile
|
|
35
|
+
fetched = fetch_env :readyfile do
|
|
36
|
+
Pathname(Dir.home) / ".readyfile"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Readyfile.open(fetched, build_dir:)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def fetch_env(key, default: nil, &block)
|
|
43
|
+
key = key.to_s.upcase
|
|
44
|
+
value = ENV.fetch("READY_#{key}") { resolve_fallback(key, default, block) }
|
|
45
|
+
raise "Expected value of #{key} to not be empty" if value.empty?
|
|
46
|
+
|
|
47
|
+
value
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def resolve_fallback(key, default, block)
|
|
51
|
+
case [default, block]
|
|
52
|
+
in String, nil then default
|
|
53
|
+
in nil, Proc then block.call
|
|
54
|
+
in nil, nil then raise "Expected ENV var #{key} to be found but was not"
|
|
55
|
+
else
|
|
56
|
+
raise ArgumentError, "args #{key.inspect}, default: #{default.inspect}, block: #{block.inspect} are invalid"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require "stringio"
|
|
2
|
+
|
|
3
|
+
module Ready
|
|
4
|
+
##
|
|
5
|
+
# Resolves an executable name (gem-provided or on-disk) to a concrete path
|
|
6
|
+
# and renders its source with a process title prelude.
|
|
7
|
+
class Executable
|
|
8
|
+
attr_reader :name, :is_gem
|
|
9
|
+
|
|
10
|
+
# Executables whose gem name differs from the command, so `path` isn't a
|
|
11
|
+
# wall of one-off special cases.
|
|
12
|
+
GEM_BIN_OVERRIDES = {
|
|
13
|
+
"bundle" => %w[bundler bundle],
|
|
14
|
+
"ri" => %w[rdoc ri],
|
|
15
|
+
"yri" => %w[yard yri],
|
|
16
|
+
"rstore" => %w[reversal-store rstore],
|
|
17
|
+
"rougify" => %w[rouge rougify],
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
SHEBANG_LINE = /^.*#!.*\n/
|
|
21
|
+
REQUIRE_RELATIVE = /(require_relative(?:\(| )\s*[\x27"]([^\s\x27"]+)[\x27"]\)?)/
|
|
22
|
+
|
|
23
|
+
def initialize(name)
|
|
24
|
+
@name = name
|
|
25
|
+
@is_gem = !File.exist?(name)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# "bin/<name>" when the executable sits directly in a bin directory, else
|
|
29
|
+
# the bare basename. Anchored on the parent directory's name so a path like
|
|
30
|
+
# /home/robin/foo doesn't match "bin" as a substring.
|
|
31
|
+
def convert_path_to_bin(path)
|
|
32
|
+
pathname = Pathname(path)
|
|
33
|
+
pathname.dirname.basename.to_s == "bin" ? "bin/#{pathname.basename}" : pathname.basename
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def render
|
|
37
|
+
stream = StringIO.new
|
|
38
|
+
stream.puts
|
|
39
|
+
stream.puts(
|
|
40
|
+
<<~RUBY,
|
|
41
|
+
Process.setproctitle #{name.inspect}
|
|
42
|
+
RUBY
|
|
43
|
+
)
|
|
44
|
+
stream.puts(source)
|
|
45
|
+
stream.string
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def path
|
|
49
|
+
@path ||= @is_gem ? gem_executable_path : on_disk_path
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def source
|
|
53
|
+
raise "No executable found for '#{@name}'" if path.nil?
|
|
54
|
+
|
|
55
|
+
@source ||= rewrite_require_relative(File.read(path).gsub(SHEBANG_LINE, "").strip)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def on_disk_path
|
|
61
|
+
if @name.to_s.include?("exe")
|
|
62
|
+
resolved = @name
|
|
63
|
+
@name = @name.split("/").last
|
|
64
|
+
return resolved
|
|
65
|
+
end
|
|
66
|
+
convert_path_to_bin(@name)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def gem_executable_path
|
|
70
|
+
override = GEM_BIN_OVERRIDES[@name]
|
|
71
|
+
return Gem.bin_path(*override) if override
|
|
72
|
+
return `rbenv which gem`.chomp if @name == "gem"
|
|
73
|
+
|
|
74
|
+
gem_path
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Rewrites `require_relative "x"` to an absolute `require`, so the source
|
|
78
|
+
# can be eval'd by the persistent server outside its original directory.
|
|
79
|
+
def rewrite_require_relative(code)
|
|
80
|
+
return code unless code.include?("require_relative")
|
|
81
|
+
|
|
82
|
+
code.scan(REQUIRE_RELATIVE).each do |statement, relpath|
|
|
83
|
+
absolute = (Pathname(path).dirname / relpath).expand_path.to_s
|
|
84
|
+
rewritten = statement.gsub("require_relative", "require").gsub(relpath, absolute)
|
|
85
|
+
code = code.gsub(statement, rewritten)
|
|
86
|
+
end
|
|
87
|
+
code
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def gem_path
|
|
91
|
+
Gem.bin_path(@name, @name)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# AUTO-GENERATED ZSH FUNCTION VIA ready.rb
|
|
2
|
+
#
|
|
3
|
+
ready_<%= name %> () {
|
|
4
|
+
emulate -L zsh
|
|
5
|
+
|
|
6
|
+
setopt pipefail errreturn warncreateglobal warnnestedvar
|
|
7
|
+
|
|
8
|
+
autoload -Uz __ready_debug
|
|
9
|
+
autoload -Uz ready_by
|
|
10
|
+
|
|
11
|
+
<% # having bundler/setup set as a require in `RUBYOPT` causes all sorts # of issues when making a stub around a persistent server.
|
|
12
|
+
# Remove it if present, careful not to slow things down by spawning subshells
|
|
13
|
+
%>
|
|
14
|
+
if (( $+parameters[RUBYOPT] ))
|
|
15
|
+
then
|
|
16
|
+
__ready_debug "Detected RUBYOPT present: ${(kv)parameters[RUBYOPT]}"
|
|
17
|
+
local -x RUBYOPT=${RUBYOPT:-""}
|
|
18
|
+
local -a rubyopt=(${(z)RUBYOPT})
|
|
19
|
+
|
|
20
|
+
rubyopt=(${rubyopt:#*bundler/setup})
|
|
21
|
+
rubyopt=(${rubyopt:#*bundler/setup.rb})
|
|
22
|
+
RUBYOPT=${(j: :)rubyopt}
|
|
23
|
+
__ready_debug "Sanitized RUBYOPT: ${(kv)parameters[RUBYOPT]}"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
<%= env.map { |pair| pair.join("=") }.join(" ") %> ready_by \
|
|
27
|
+
-e <%= ruby.shellescape %> "$@"
|
|
28
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
##
|
|
2
|
+
# A single executable declared in a readyfile, mapping its name to the
|
|
3
|
+
# compiled path under the build directory.
|
|
4
|
+
class Ready::Readyfile::Executable
|
|
5
|
+
attr_reader :name, :prefix, :build_dir
|
|
6
|
+
|
|
7
|
+
def initialize(name, build_dir:, prefix: "ready_")
|
|
8
|
+
@name = name
|
|
9
|
+
@prefix = prefix
|
|
10
|
+
@build_dir = Pathname(build_dir)
|
|
11
|
+
|
|
12
|
+
validate!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def mapping
|
|
16
|
+
{ name => realpath }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ready_name
|
|
20
|
+
prefix + name
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def realpath
|
|
24
|
+
build_dir / ready_name
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def validate!
|
|
30
|
+
validate_name!
|
|
31
|
+
validate_build_dir!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate_name!
|
|
35
|
+
raise ArgumentError, "Name cannot be nil" if name.nil?
|
|
36
|
+
raise ArgumentError, "Name cannot be empty" if name.empty?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def validate_build_dir!
|
|
40
|
+
raise ArgumentError, "build_dir cannot be nil for executable #{name.inspect}" if build_dir.nil?
|
|
41
|
+
return if build_dir.directory?
|
|
42
|
+
|
|
43
|
+
raise ArgumentError,
|
|
44
|
+
"build_dir #{build_dir.inspect} must exist for executable #{name.inspect}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
module Ready
|
|
4
|
+
##
|
|
5
|
+
# Reads the YAML readyfile and exposes the gems and executables it declares.
|
|
6
|
+
class Readyfile
|
|
7
|
+
attr_reader :config, :build_dir, :path
|
|
8
|
+
|
|
9
|
+
def self.open(path, build_dir:)
|
|
10
|
+
path = Pathname(path)
|
|
11
|
+
# A missing, empty, or null readyfile is not an error: it simply declares
|
|
12
|
+
# no gems or executables. YAML.parse_file returns false for an empty file,
|
|
13
|
+
# and a document such as "---" parses to nil.
|
|
14
|
+
# YAML.parse_file returns `false` for an empty file (not nil), so guard on
|
|
15
|
+
# truthiness, not with `&.` -- false&.to_ruby would blow up. A "---"
|
|
16
|
+
# document is truthy but to_ruby's to nil, which the `|| {}` folds to empty.
|
|
17
|
+
document = (YAML.parse_file(path.to_s) if path.exist?)
|
|
18
|
+
config = (document ? document.to_ruby : {}) || {}
|
|
19
|
+
|
|
20
|
+
unless config.is_a?(Hash)
|
|
21
|
+
raise Error, "#{path}: readyfile must be a YAML mapping of gems:/executables:, got #{config.class}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
new config, build_dir:, path:
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(config, build_dir:, path:)
|
|
28
|
+
@path = path
|
|
29
|
+
@config = config
|
|
30
|
+
@build_dir = build_dir
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_path
|
|
34
|
+
path
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_s
|
|
38
|
+
path.to_s
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def gem_names
|
|
42
|
+
config.fetch("gems", [])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def executables
|
|
46
|
+
config
|
|
47
|
+
.fetch("executables", [])
|
|
48
|
+
.map { Executable.new it, build_dir: }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def executable_names
|
|
52
|
+
executables.map(&:name)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def executable_paths
|
|
56
|
+
executables.map(&:realpath)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def each_executable_mapping(&)
|
|
60
|
+
return enum_for __method__ unless block_given?
|
|
61
|
+
|
|
62
|
+
executables.flat_map(&:mapping).each do |mapping|
|
|
63
|
+
yield(*mapping.to_a)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def each_executable
|
|
68
|
+
return enum_for __method__ unless block_given?
|
|
69
|
+
|
|
70
|
+
executables.each { yield it }
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/ready/version.rb
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
|
|
3
|
+
module Ready
|
|
4
|
+
##
|
|
5
|
+
# Renders a zsh function that wraps an executable, using the bundled ERB
|
|
6
|
+
# template.
|
|
7
|
+
class ZshFunction
|
|
8
|
+
attr_reader :name, :executable, :environment, :template_path
|
|
9
|
+
|
|
10
|
+
DEFAULT_TEMPLATE_PATH = Pathname(__dir__) / "fn.zsh.erb"
|
|
11
|
+
def initialize(name, environment: {}, template_path: DEFAULT_TEMPLATE_PATH)
|
|
12
|
+
@name = name
|
|
13
|
+
@environment = environment
|
|
14
|
+
@executable = Executable.new(name)
|
|
15
|
+
@template_path = Pathname(template_path)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
template = ERB.new(@template_path.read)
|
|
20
|
+
template.result_with_hash(
|
|
21
|
+
name: name,
|
|
22
|
+
ruby: executable.render,
|
|
23
|
+
env: environment,
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require "stringio"
|
|
2
|
+
|
|
3
|
+
module Ready
|
|
4
|
+
##
|
|
5
|
+
# Assembles a zsh script by rendering one ZshFunction per name, building the
|
|
6
|
+
# functions concurrently.
|
|
7
|
+
class ZshScript
|
|
8
|
+
attr_accessor :names
|
|
9
|
+
attr_reader :environment
|
|
10
|
+
|
|
11
|
+
def initialize(names: [], environment: {})
|
|
12
|
+
@names = names
|
|
13
|
+
@environment = environment
|
|
14
|
+
@mutex = Mutex.new
|
|
15
|
+
@script = StringIO.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def multithread_process(names)
|
|
19
|
+
threads = names.map do |name|
|
|
20
|
+
Thread.new do
|
|
21
|
+
zsh_body = ZshFunction.new(name, environment: environment).to_s
|
|
22
|
+
@mutex.synchronize do
|
|
23
|
+
@script.puts zsh_body
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
threads.each(&:join)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def set_env(key, value)
|
|
32
|
+
@environment[key] = value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add_eager_loading!
|
|
36
|
+
# @source.puts 'by -e "ActiveSupport.eager_load!"'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_s
|
|
40
|
+
add_eager_loading!
|
|
41
|
+
multithread_process(@names)
|
|
42
|
+
@script.string
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/ready.rb
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
require "zeitwerk"
|
|
2
|
+
require "pathname"
|
|
3
|
+
require "shellwords"
|
|
2
4
|
|
|
3
5
|
##
|
|
4
6
|
# Top-level namespace for the ready gem.
|
|
5
7
|
module Ready
|
|
6
8
|
LOADER = Zeitwerk::Loader.for_gem
|
|
9
|
+
LOADER.inflector.inflect("cli" => "CLI")
|
|
7
10
|
LOADER.setup
|
|
8
11
|
|
|
9
12
|
##
|
|
10
13
|
# Base error class for ready.
|
|
11
14
|
class Error < StandardError; end
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# The root directory of the ready gem (the parent of `lib/`). Used to locate
|
|
18
|
+
# bundled assets such as the zsh plugin and the Rakefile.
|
|
19
|
+
#
|
|
20
|
+
# @return [Pathname]
|
|
21
|
+
def self.root
|
|
22
|
+
Pathname(__dir__).parent
|
|
23
|
+
end
|
|
12
24
|
end
|