Skip to content

Instantly share code, notes, and snippets.

@danhodge
Last active July 1, 2020 20:24
Show Gist options
  • Save danhodge/5f01fe4739a7d5a22f8739242d79b193 to your computer and use it in GitHub Desktop.
Save danhodge/5f01fe4739a7d5a22f8739242d79b193 to your computer and use it in GitHub Desktop.
Ruby Tricks
# Define a method as both an instance & class method
class Foo
def self.bar
end
# Note: this requires Rails/Active Support - not sure if it can be done with Forwardable
delegate :bar, to: "self.class"
end
# Named parameter string substitution
template = "Hello, %{name}. The time is now %{time}"
str = template % { name: "Ruby", time: Time.now }
# Define a custom exception matcher
# Needs to be a class or module with a self.=== method that takes an exception instance and returns true or false on match
# Sample implementation - handler class
class Handler
attr_reader :bad_request_exceptions
def initialize(bad_request_exceptions: [])
@bad_request_exceptions = bad_request_exceptions
end
def on_bad_request(ex)
puts "Bad Request: #{ex.message}"
end
end
# Matcher "factory"
module Matcher
def self.bad_request(handler)
matcher = Module.new
matcher.define_singleton_method(:===) do |ex|
handler.bad_request_exceptions.any? { |type| type === ex }
end
matcher
end
end
# Error handler
def with_error_handler(handler)
yield
rescue Matcher.bad_request(handler) => ex
handler.on_bad_request(ex)
rescue => ex
puts "Unexpected Error"
end
# Usage
with_error_handler(Handler.new(bad_request_exceptions: [Excon::Error::BadRequest])) do
do_it
end
# Give an anonymous class a name
klass = Class.new do
def self.name
"NoName"
end
end
klass.new.class.name # => "NoName"
# Manually set or override cause on a new exception
raise ExceptionClass, cause: ex
# Introspect the keyword arguments for a method
class SomeClass
def some_method(arg1:, arg2: 3, arg3: nil)
end
end
SomeClass.instance_method(:some_method).parameters # => [[:keyreq, :arg1], [:key, :arg2], [:key, :arg3]]
# Lazily select the first n unique elements from a collection
uniques = Set.new
collection.lazy.select { |item| uniques.add?(item) }.take(n)
# Set the system timezone to UTC
TZ=UTC ruby ...
# Inter-process communication
# Use the Open3.capture methods for all "one-shot" commands (the popen methods are prone to deadlocks)
# The following can be used to run a command the accepts input from stdin and writes output to stdout & stderr
stdout, stderr, status = Open3.capture3('cmd', 'arg1', 'arg2', stdin_data: "some input")
# To "prepend" class methods, you need to prepend the module into the eigenclass
module Other
def do_something
puts "About to do something"
super
puts "Just did something"
end
end
class Something
singleton_class.prepend Other
def self.do_something
puts "Doing something"
end
end
# To prepend instance & class methods, the prepended hook can be used
module Other2
def self.prepended(base)
base.singleton_class.prepend(ClassMethods)
end
def do_something_else
end
module ClassMethods
def do_something
end
end
end
class Something2
prepend Other2
def self.do_something
end
def do_something_else
end
end
# When prepending multiple modules, the last one is first
module Bar
def doit(x)
puts "Bar: #{x}"
super(x + 1)
end
end
module Baz
def doit(x)
puts "Baz: #{x}"
super(x - 3)
end
end
class Foo
prepend Bar
prepend Baz
def doit(x)
puts "Foo: #{x}"
x * 2
end
end
> Foo.new.doit(5)
Baz: 5
Bar: 2
Foo: 3
# => 6
# Use Enumerable#chunk to break a list of items into approximately equally-sized chunks
total_length = 0
chunks = array_of_strings.chunk do |str|
total_length += str.length
total_length / 100
end
chunks.map(&:last) #=> an Array of Arrays of Strings, the combined length of each sub-Array roughly equal to 100
# Unicode code points with more than 4 hex digits (many emojis) need to be enclosed in {}
"\u{1f601}" #=> "😁"
"\u{1f601 1f602}" #=> "😁😂"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment