Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Last active December 9, 2017 17:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/b31103fe4aab24f0d5cc650c442bddaa to your computer and use it in GitHub Desktop.
Save JoshCheek/b31103fe4aab24f0d5cc650c442bddaa to your computer and use it in GitHub Desktop.
Backticks in Ruby
# https://twitter.com/yburyug/status/939517726846005248
# It does execute it's arg in the shell
`echo hello` # => "hello\n"
# It doesn't work in your example b/c it is private
Object.new.` "echo world" rescue $! # => #<NoMethodError: private method ``' called for #<Object:0x007f8c900257c8>>
#`# fix the hilighting
# Lets make it public
public :`
# It's private, because it isn't intended as a method you call on other objects
# Like... the fact that I can now do this is pretty ridiculous
Object.new.` "echo world" # => "world\n"
123.` "echo hello | tr heo wod | sed s/l/r/" # => "world\n"
Object.` "ls" # => "program.rb\n"
#`# fix the hilighting
# So, why is it a method on every object, then? Because it comes from Kernel
method(:`).owner # => Kernel
# It's intended to be used like a set of latently available functionality,
# same as these other things we use like they're available from the language,
# without thinking too much about why
method(:require).owner # => Kernel
method(:puts).owner # => Kernel
method(:lambda).owner # => Kernel
method(:system).owner # => Kernel
# So by placing it in Kernel, it's available to the contexts we write code in,
# hence allowing us to do things like this:
`echo implicitly calling backticks on self` # => "implicitly calling backticks on self\n"
# And by making it private, the outside world can't see that it's part of our
# object, avoiding the confusion of what does it mean that I can call backticks
# on things like hashes and numbers.
# ----- Soooooooo, lets fuck with it! -----
module Kernel
require 'shellwords'
def `(arg)
arg.shellsplit
.slice_before { |token| token == '|' }
.to_a
.each { |tokens| tokens.shift if tokens[0] == '|' }
.reduce([self, []]) { |(receiver, results), (message, *args)|
args.map! { |arg| arg=~/\A\d+\Z/ ? arg.to_i : arg }
result = receiver.send message, *args
[(result.clone rescue result), results<<result]
}
.last
end
end
`inspect | chars | join - | upcase`
# => ["main", ["m", "a", "i", "n"], "m-a-i-n", "M-A-I-N"]
# ALSO, fun fact: backticked heredocs
<<~`BASH`
itself |
inspect |
reverse |
delete i |
+ e |
split |
<< World |
<< greeting |
<< Hello |
each_slice 2 | to_h
BASH
# => [main,
# "main",
# "niam",
# "nam",
# "name",
# ["name"],
# ["name", "World"],
# ["name", "World", "greeting"],
# ["name", "World", "greeting", "Hello"],
# #<Enumerator: ...>,
# {"name"=>"World", "greeting"=>"Hello"}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment