Skip to content
A Zsh-Zplugin annex (i.e. extension) that provides rvm-like solution for ruby, node and also regular binaries
Shell
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
images images: z-a-bin-gem-node.png -> diag.png Sep 18, 2019
.gitignore LICENSE, .gitignore Sep 15, 2019
.za-bgn-bin-or-src-function-body *function-body: Set also PATH Oct 30, 2019
.za-bgn-mod-function-body *mod*body,*atload-handler: fmod'' to support E,N,O flags Sep 25, 2019
:za-bgn-atclone-handler *atclone-handler: Handle all the results of sbin'' expansion Nov 14, 2019
:za-bgn-atdelete-handler *-handler: Fix the loop Nov 10, 2019
:za-bgn-atload-handler *-handler: Fix the loop Nov 10, 2019
:za-bgn-help-handler :za-bgn-help-handler Oct 17, 2019
LICENSE LICENSE, .gitignore Sep 15, 2019
README.md
z-a-bin-gem-node.plugin.zsh *{atclone,atdelete}-handler,plugin.zsh: Automatic-empty sbin ice Nov 9, 2019

README.md

z-a-bin-gem-node

A Zsh-Zplugin annex (i.e. an extension) that provides functionality, which allows to:

  1. Run programs and scripts without adding anything to $PATH,
  2. Install and run Ruby gems and Node modules from within a local directory with $GEM_HOME and $NODE_PATH automatically set,
  3. Run programs, scripts and functions with automatic cd into the plugin or snippet directory, plus also with automatic standard output & standard error redirecting.
  4. Source scripts through an automatically created function with the above $GEM_HOME, $NODE_PATH and cd features available,
  5. Create the so called shims known from rbenv – the same feature as the first item of this enumaration – of running a program without adding anything to $PATH with all of the above features, however through an automatic script created in $ZPFX/bin, not a function (the first item uses a function-based mechanism),
  6. Automatic updates of Ruby gems and Node modules during regular plugin and snippet updates with zplugin update ….

How it works – bird's-eye view

Below is a diagram explaining the major feature – exposing a binary program or script through a Zsh function of the same name:

diagram

This way there is no need to add anything to $PATHz-a-bin-gem-node will automatically create a function that will wrap the binary and provide it on the command line like if it was being placed in the $PATH.

Also, like mentioned in the enumeration, the function can automatically export $GEM_HOME, $NODE_PATH shell variables and also automatically cd into the plugin or snippet directory right before executing the binary and then cd back to the original directory after the execution is finished.

Also, like already mentioned, instead of the function an automatically created script – so called shim – can be used for the same purpose and with the same functionality.

How it works, in detail

Suppose that you would want to install junegunn/fzf-bin plugin from GitHub Releases, which contains only single file – the fzf binary for the selected architecture. It is possible to do it in the standard way – by adding the plugin's directory to the $PATH:

zplugin ice as"command" from"github-rel"
zplugin load junegunn/fzf-bin

After this command, the $PATH variable will contain e.g.:

% print $PATH
/home/sg/.zplugin/plugins/junegunn---fzf-bin:/bin:/usr/bin:/usr/sbin:/sbin

For many such programs loaded as plugins the PATH can become quite cluttered. I've had 26 entries before switching to z-a-bin-gem-node. To solve this, load with use of fbin'' ice provided and handled by z-a-bin-gem-node:

zplugin ice from"gh-r" fbin"fzf"
zplugin load junegunn/fzf-bin

The $PATH will remain unchanged and an fzf function will be created:

% which fzf
fzf () {
        local bindir="/home/sg/.zplugin/plugins/junegunn---fzf-bin"
        "$bindir"/"fzf" "$@"
}

Running the function will forward the call to the program accessed through an embedded path to it. Thus, no $PATH changes are needed!

The Ice Modifiers Provided By The Annex

There are 7 ice-modifiers provided and handled by the annex. They are:

  1. fbin'' – creates functions for binaries and scripts,
  2. sbin'' – creates shims for binaries and scripts,
  3. gem'' – installs and updates gems + creates functions for gems' binaries,
  4. node'' – installs and updates node_modules + creates functions for binaries of the modules,
  5. fmod'' – creates wrapping functions for other functions,
  6. fsrc'' – creates functions that source given scripts,
  7. ferc'' – the same as fsrc'', but using an alternate script-loading method.

The ice-modifiers in detail:


1. fbin"[{g|n|c|N|E|O}:]{path-to-binary}[ -> {name-of-the-function}]; …"

Creates a wrapper function of the name the same as the last segment of the path or as {name-of-the-function}. The optional preceding flags mean:

  • g – set $GEM_HOME variable to {plugin-dir},
  • n – set $NODE_PATH variable to {plugin-dir}/node_modules,
  • c – cd to the plugin's directory before running the program and then cd back after it has been run,
  • N – append &>/dev/null to the call of the binary, i.e. redirect both standard output and standard error to /dev/null,
  • E – append 2>/dev/null to the call of the binary, i.e. redirect standard error to /dev/null,
  • O – append >/dev/null to the call of the binary, i.e. redirect standard output to /dev/null.

Example:

% zplugin ice from"gh-r" fbin"g:fzf -> myfzf"
% zplugin load junegunn/fzf-bin
% which myfzf
myfzf () {
        local bindir="/home/sg/.zplugin/plugins/junegunn---fzf-bin"
        local -x GEM_HOME="/home/sg/.zplugin/plugins/junegunn---fzf-bin"
        "$bindir"/"fzf" "$@"
}

The ice can be empty. It will then try to create the function for:

  • trailing component of the id_as ice, e.g.: id_as'exts/git-my' → it'll check if a file git-my exists and if yes, create the function git-my,
  • the plugin name, e.g.: for paulirish/git-open it'll check if a file git-open exists and if yes, create the function git-open,
  • trailing component of the snippet URL,
  • for any alphabetically first executable file.

2. gem"{gem-name}; …"

    gem"[{path-to-binary} <-] !{gem-name} [-> {name-of-the-function}]; …"

Installs the gem of name {gem-name} with $GEM_HOME set to the plugin's or snippet's directory. In other words, the gem and its dependencies will be installed locally in that directory.

In the second form it also creates a wrapper function identical to the one created with fbin'' ice.

Example:

% zplugin ice gem'!asciidoctor'
% zplugin load zdharma/null
% which asciidoctor
asciidoctor () {
        local bindir="/home/sg/.zplugin/plugins/zdharma---null/bin" 
        local -x GEM_HOME="/home/sg/.zplugin/plugins/zdharma---null" 
        "$bindir"/"asciidoctor" "$@"
}

3. node"{node-module}; …"

    node"[{path-to-binary} <-] !{node-module} [-> {name-of-the-function}]; …"

Installs the node module of name {node-module} inside the plugin's or snippet's directory.

In the second form it also creates a wrapper function identical to the one created with fbin'' ice.

Example:

% zplugin delete zdharma/null
Delete /home/sg/.zplugin/plugins/zdharma---null?
[yY/n…]
y
Done (action executed, exit code: 0)
% zplugin ice node'remark <- !remark-cli -> remark; remark-man'
% zplugin load zdharma/null
…installation messages…
% which remark
remark () {
        local bindir="/home/sg/.zplugin/plugins/zdharma---null/node_modules/.bin"
        local -x NODE_PATH="/home/sg/.zplugin/plugins/zdharma---null"/node_modules
        "$bindir"/"remark" "$@"
}

In this case the name of the binary program provided by the node module is different from its name, hence the second form with the b <- a -> c syntax has been used.


4. fmod"[{g|n|c|N|E|O}:]{function-name}; …"

    fmod"[{g|n|c|N|E|O}:]{function-name} -> {wrapping-function-name}; …"

It wraps given function with the ability to set $GEM_HOME, etc. – the meaning of the g,n and c flags is the same as in the fbin'' ice.

Example:

% myfun() { pwd; ls -1 }
% zplugin ice fmod'cgn:myfun'
% zplugin load zdharma/null
% which myfun
myfun () {
        local -x GEM_HOME="/home/sg/.zplugin/plugins/zdharma---null"
        local -x NODE_PATH="/home/sg/.zplugin/plugins/zdharma---null"/node_modules
        local oldpwd="/home/sg/.zplugin/plugins/zplugin---z-a-bin-gem-node"
        () {
                setopt localoptions noautopushd
                builtin cd -q "/home/sg/.zplugin/plugins/zdharma---null"
        }
        "myfun--za-bgn-orig" "$@"
        () {
                setopt localoptions noautopushd
                builtin cd -q "$oldpwd"
        }
}
% myfun
/home/sg/.zplugin/plugins/zdharma---null
LICENSE
README.md

5. sbin"[{g|n|c|N|E|O}:]{path-to-binary}[ -> {name-of-the-script}]; …"

It creates the so called shim known from rbenv – a wrapper script that forwards the call to the actual binary. The script is created always under the same, standard and single $PATH entry: $ZPFX/bin (which is ~/.zplugin/polaris/bin by default).

The flags have the same meaning as with fbin'' ice.

Example:

% zplugin delete junegunn/fzf-bin
Delete /home/sg/.zplugin/plugins/junegunn---fzf-bin?
[yY/n…]
y
Done (action executed, exit code: 0)
% zplugin ice from"gh-r" sbin"fzf"
% zplugin load junegunn/fzf-bin
…installation messages…
% cat $ZPFX/bin/fzf
#!/usr/bin/env zsh

function fzf {
    local bindir="/home/sg/.zplugin/plugins/junegunn---fzf-bin"
    "$bindir"/"fzf" "$@"
}

fzf "$@"

The ice can be empty. It will then try to create the shim for:

  • trailing component of the id_as ice, e.g.: id_as'exts/git-my' → it'll check if a file git-my exists and if yes, create the shim git-my,
  • the plugin name, e.g.: for paulirish/git-open it'll check if a file git-open exists and if yes, create the shim git-open,
  • trailing component of the snippet URL,
  • for any alphabetically first executable file.

6. fsrc"[{g|n|c|N|E|O}:]{path-to-script}[ -> {name-of-the-function}]; …"

7. ferc"[{g|n|c|N|E|O}:]{path-to-script}[ -> {name-of-the-function}]; …"

Creates a wrapper function that at each invocation sources the given file. The second ice, ferc'' works the same with the single difference that it uses eval "$(<{path-to-script})" instead of source "{path-to-script}" to load the script.

Example:

% zplugin ice fsrc"myscript -> myfunc" ferc"myscript"
% zplugin load zdharma/null
% which myfunc
myfunc () {
        local bindir="/home/sg/.zplugin/plugins/zdharma---null"
        () {
                source "$bindir"/"myscript"
        } "$@"
}
% which myscript
myscript () {
        local bindir="/home/sg/.zplugin/snippets/OMZ::plugins--git/git.plugin.zsh"
        () {
                eval "$(<"$bindir"/"myscript")"
        } "$@"
}

The ices can be empty. They will then try to create the function for trailing component of the id-as ice and the other cases, in the same way as with the fbin ice.

Installation

Simply load like a regular plugin, i.e.:

zplugin light zplugin/z-a-bin-gem-node

After executing this command you can then use the ice-mods.

You can’t perform that action at this time.