The majority of these Ruby Tricks were extracted from James Edward Gray II talk. If you know some other tricks, please contribute!
- Alphanumeric incrementing
- Associative arrays
- Autovivification
- Blocks can take blocks
- Bubbling up thread errors
- Case on ranges
- Count all objects
- Cycle
- Data
- Easiest database pstore
- Easiest database pstore yaml
- Enable garbage collector profiler
- Enable ruby warnings
- Fast memoization fibonacci
- Fetch data
- Get random data
- Head tail
- Inject
- Inspecting the source with script lines
- Iterating over specific types
- Lambda your own syntax
- Memoization
- Print formatted with debug
- Ruby debug flag
- Shortcut variable interpolation
- Single instance running
- Smalltalk conditionals
- Splat operator
- Stab operator
- Struct without assignment
- Super magic keyword
- Super magic keyword2
- Super magic keyword3
- Super magic keyword4
- Super magic keyword5
- Tail call
- Trigger irb as needed
- Unused variable format
- Variables from a regex
- Zip
"1".next
#=> "2"
"a".next
#=> "b"
"1a".next
#=> "1b"
"1z".next
#=> "2a"
"1aa".next
#=> "1ab"
"1az".next
#=> "1ba"
"1aaz".next
#=> "1aba"
aa = [ %w[Someone 1],
%w[Bla 2]]
p aa.assoc("Someone")
p aa.assoc("Bla")
# Result:
# ["Someone", "1"]
# ["Bla", "2"]
p aa.rassoc("1")
p aa.rassoc("2")
# Result:
# ["Someone", "1"]
# ["Bla", "2"]
deep = Hash.new { |hash,key| hash[key] = Hash.new(&hash.default_proc) }
deep[:a][:b][:c][:d] = 42
p deep
# Result:
# {:a=>{:b=>{:c=>{:d=>42}}}}
var = :var
object = Object.new
object.define_singleton_method(:show_var_and_block) do |&block|
p [var, block]
end
object.show_var_and_block { :block }
# Result:
# [:var, #<Proc:0x007ffd6c038128@./blocks_can_take_blocks.rb:8>]
Thread.abort_on_exception = true
Thread.new do
fail 'Ops, we cannot continue'
end
loop do
sleep
end
# Result:
# ./bubbling_up_thread_errors.rb:4:in `block in <main>': Ops, we cannot continue (RuntimeError)
age = rand(1..100)
p age
case age
when -Float::INFINITY..20
p 'You are too young'
when 21..64
p 'You are at the right age'
when 65..Float::INFINITY
p 'You are too old'
end
# Result:
# 55
# "You are at the right age"
require 'pp'
pp ObjectSpace.count_objects
# Result:
# {:TOTAL=>30163,
# :FREE=>1007,
# :T_OBJECT=>39,
# :T_CLASS=>534,
# :T_MODULE=>24,
# :T_FLOAT=>4,
# :T_STRING=>9290,
# :T_REGEXP=>70,
# :T_ARRAY=>2231,
# :T_HASH=>53,
# :T_STRUCT=>1,
# :T_BIGNUM=>2,
# :T_FILE=>14,
# :T_DATA=>966,
# :T_MATCH=>1,
# :T_COMPLEX=>1,
# :T_NODE=>15896,
# :T_ICLASS=>30}
ring = %w[one two three].cycle
p ring.take(5)
# Result:
# ["one", "two", "three", "one", "two"]
puts DATA.read
__END__
Hey oh!
Hey oh!
require 'pstore'
db = PStore.new('mydatabase.pstore')
db.transaction do
db['people1'] = 'Someone'
db['money1'] = 400
end
db.transaction do
db['people2'] = 'Someone2'
db['money2'] = 300
end
db.transaction(true) do
p 'People %p' % db['people1']
p 'Money %p' % db['money1']
p "SECOND PERSON"
p 'People %p' % db['people2']
p 'Money %p' % db['money2']
end
# Result:
# "People \"Someone\""
# "Money 400"
# "SECOND PERSON"
# "People \"Someone2\""
# "Money 300"
require 'yaml/store'
db = YAML::Store.new('people.yml')
db.transaction do
db['people1'] = 'Someone'
db['money1'] = 400
end
db.transaction do
db['people2'] = 'Someone2'
db['money2'] = 300
end
db.transaction(true) do
p 'People %p' % db['people1']
p 'Money %p' % db['money1']
p "SECOND PERSON"
p 'People %p' % db['people2']
p 'Money %p' % db['money2']
end
# Result:
# "People \"Someone\""
# "Money 400"
# "SECOND PERSON"
# "People \"Someone2\""
# "Money 300"
GC::Profiler.enable
10.times do
array = Array.new(1_000_000) { |i| i.to_s }
end
puts GC::Profiler.result
$VERBOSE = true
class WarnMe
def var
@var || 42
end
end
p WarnMe.new.var
# Result:
# ./enable_ruby_warnings.rb:5: warning: instance variable @var not initialized
# 42
fibonacci = Hash.new{ |numbers,index|
numbers[index] = fibonacci[index - 2] + fibonacci[index - 1]
}.update(0 => 0, 1 => 1)
p fibonacci[300]
# Result:
# 222232244629420445529739893461909967206666939096499764990979600
params = {var: 42}
p params.fetch(:var)
p params.fetch(:missing, 42)
p params.fetch(:missing) { 40 + 2 }
params.fetch(:missing)
# Result:
# 42
# 42
# 42
# ./fetch_data.rb:7:in `fetch': key not found: :missing (KeyError)
# from ./fetch_data.rb:7:in `<main>'
require 'securerandom'
p SecureRandom.random_number
p SecureRandom.random_number(100)
p
p SecureRandom.hex(20)
p SecureRandom.base64(20)
# Result:
# 0.7851536586163714
# 46
# "3efb674fbc2ba390856c15489652e75e8afff6d1"
# "yFv0WzugzFC6/D71teVe1Y5r1kU="
def my_reduce(array)
head, *tail = array
return (tail.empty? ? head : (head + my_reduce(tail)))
end
# triangular number example
n = 100
my_reduce((1..n).to_a) == (n*(n+1))/2 #=> True
p (1..10).inject{ |r,e| p [r,e]; r*2}
# Result:
# [1, 2]
# [2, 3]
# [4, 4]
# [8, 5]
# [16, 6]
# [32, 7]
# [64, 8]
# [128, 9]
# [256, 10]
# 512
SCRIPT_LINES__ = { }
#require_relative = 'better_be_well_formed_code'
require_relative = 'better_be_well_formed_code_with_a_line_size_greather_than_80_it_is_not_good'
if SCRIPT_LINES__.values.flatten.any? { |line| line.size > 80}
abort 'Clean up your code first!'
end
ObjectSpace.each_object(String) do |object|
p object
end
# Result:
# "block in dependent_specs"
# "block in dependent_specs"
# "block (3 levels) in dependent_gems"
# "block (3 levels) in dependent_gems"
# ... (huge output suppressed)
# "This rdoc is bundled with Ruby"
# encoding UTF-8
module Kernel
alias_method :λ, :lambda
end
l = λ { p :called }
l.call
# Result:
# :called
# based on Justin Weiss' article:
# http://www.justinweiss.com/articles/4-simple-memoization-patterns-in-ruby-and-one-gem/
class Memoize
# one liner
def my_simple_method
@my_simple_method ||= do_some_calculation
end
# multiple lines
def my_more_complex_method
@my_more_complex_method ||= begin
a = do_some_calculation
b = do_some_more_calculation
a + b
end
end
# what if our calculations return nil?...
# one liner
def my_simple_method
return @my_simple_method if defined? @my_simple_method
@my_simple_method = do_some_calculation
end
# multiple lines
def my_more_complex_method
return @my_more_complex_method if defined? @my_more_complex_method
@my_more_complex_method = begin
a = do_some_calculation
b = do_some_more_calculation
a + b
end
end
# what about differing arguments?...
def my_really_complex_method(*args)
@my_really_complex_method ||= Hash.new do |h, key|
h[key] = do_some_calculation(*key)
end
@my_really_complex_method[args]
end
end
def debug(name, content)
p "%s: %p" % [name, content]
end
debug "Num", 42
# Result:
# "Num: 42"
def var
@var || 40
end
if $DEBUG
p "var is %p" % var
end
p var + 2
# Result:
# ruby_debug_flag.rb:2: warning: instance variable @var not initialized
# "var is 40"
# ruby_debug_flag.rb:2: warning: instance variable @var not initialized
# 42
@instance = :instance
@@class = :class
$global = :global
p "#@instance, #@@class, and #$global variables don't need braces"
# Result:
# "instance, class, and global variables don't need braces"
DATA.flock(File::LOCK_EX | File::LOCK_NB) or abort 'Already running'
trap('INT', 'EXIT')
puts 'Running...'
loop do
sleep
end
__END__
DO NOT DELETE: used for locking
def true.-(a, &b); a[] end
def false.-(a, &b); b[] end
puts (1 == 1).--> { :ok } { :different }
puts (4 == 2).--> { :ok } { :different }
# Result:
# # ok
# # different
# Splat Operator (*)
# When calling methods
arguments = [1, 2, 3, 4]
my_method(*arguments) # any number of arguments
# or:
arguments = [2, 3, 4]
my_method(1, *arguments) # any number of trailing arguments
# or:
arguments = [1, 2]
my_method(*arguments, 3, 4) # any number of preceding arguments
# or:
arguments = [2, 3]
my_method(1, *arguments, 4) # any number of "in between" arguments
# All are equivalent to:
my_method(1, 2, 3, 4)
# Two splats (**) convert a hash into an arbitary number of keyword arguments
# This operator doesn't technically have a name
arguments = { first: 1, second: 2, third: 3 }
my_method(**arguments)
# or:
arguments = { first: 1, second: 2 }
my_method(third: 3, **arguments)
# Are equivalent to:
my_method(first:1, second:2, three:3)
# Stab Operator - Lambdas in Ruby 1.9 or later.
# Y Combinator
# Ruby supports a syntax for lambdas known as the 'stab' operator.
# Rather than something like lambda { a < 5 },
# you can type -> { a < 5 }.
#
# Below is a version of the fibonacci sequence that can
# perform recursive calls without named functions.
#
# Improver function for fibonacci sequence
# Assumes that the 0th element of the sequence is 0,
# and the 1st element of the sequence is 1.
fib_improver = ->(partial) {
->(n) { n < 2 ? n : partial.(n-1) + partial.(n-2) }
}
# The y combinator
y = ->(f) {
->(x) { x.(x) }.(
->(x) { f.(->(v) { x.(x).(v)}) }
)
}
# Using the stab operator and y combinator, we can
# write a fibonacci function with anonymous functions
# This solution is not memoized and so will be very slow.
fib = fib_improver.(y.(fib_improver))
p fib.(1)
p fib.(10)
# Notice that after loading, fib isn't defined anymore.
Struct.new("Name", :first, :last) do
def full
"#{first} #{last}"
end
end
franzejr = Struct::Name.new("Franze", "Jr")
p franzejr.full
# Result:
# "Franze Jr"
class Parent
def show_args(*args)
p args
end
end
class Child < Parent
def show_args(a,b,c)
super(a,b,c)
end
end
Child.new.show_args(:a, :b, :c)
# Result:
# [:a, :b, :c]
class Parent
def show_args(*args, &block)
p [*args, block]
end
end
class Child < Parent
def show_args(a,b,c)
super
end
end
#Everything goes up, including the block
Child.new.show_args(:a, :b, :c) { :block }
# Result:
# [:a, :b, :c, #<Proc:0x007fbf7a0486e8@super_magic_keyword2.rb:14>]
class Parent
def show_args(*args, &block)
p [*args, block]
end
end
class Child < Parent
def show_args(a,b,c)
# Call super without any params
# making args an empty array []
super()
end
end
#Nothing goes up
Child.new.show_args(:a, :b, :c)
# Result:
# [nil]
class Parent
def show_args(*args, &block)
p [*args, block]
end
end
class Child < Parent
def show_args(a,b,c)
# modify super by passing nothing
# calling super with a nil proc,
# which is basically calling super()
super(&nil)
end
end
#Nothing goes up, neither the block
Child.new.show_args(:a, :b, :c) { :block }
# Result:
# [nil]
class DontDelegateToMe; end
class DelegateToMe; def delegate; "DelegateToMe" end end
module DelegateIfCan
def delegate
if defined? super
"Modified: #{super}"
else
"DelegateIfCan"
end
end
end
p DelegateToMe.new.extend(DelegateIfCan).delegate
p DontDelegateToMe.new.extend(DelegateIfCan).delegate
# Result:
# "Modified: DelegateToMe"
# "DelegateIfCan"
RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true,
trace_instruction: false }
eval <<end
def factorial(n, result=1)
if n==1
result
else
factorial(n-1, n*result)
end
end
end
p factorial(100000)
# Result:
require 'irb'
def my_program_context
@my_program_context ||= Struct.new(:value).new(40)
end
trap(:INT) do
IRB.start
trap(:INT, 'EXIT')
end
loop do
p "Current value: #{my_program_context.value}"
sleep 1
end
# Result:
# "Current value: 40"
# "Current value: 40"
[
['Someone', 41, 'another field'],
['Someone2', 42, 'another field2'],
['Someone3', 43, 'another field3']
].each do |name,_,_|
p name
end
# Result:
# "Someone"
# "Someone2"
# "Someone3"
if /\A(?<first>\w+),\s*(?<last>\w+)\z/ =~ "Franze, Jr"
puts "#{first} #{last}"
end
# Result:
# Franze Jr
letters = "a".."d"
numbers = 1..3
letters.zip(numbers) do |letter, number|
p(letter: letter, number: number)
end
# Result:
# {:letter=>"a", :number=>1}
# {:letter=>"b", :number=>2}
# {:letter=>"c", :number=>3}
# {:letter=>"d", :number=>nil}
- @JEG2
- @franzejr
- @rafaelsales
- @jomagam
- @fredkelly
- @shadefinale
- @matugm
- @lightyrs
- @ilyakava
- @dansandland
- @xzgyb
- @filipebarcos
- @ezekg
- @0x0dea
- Fork it
- Create your trick branch:
git checkout -b my-ruby-trick
- Add your trick to the collection of
.rb
files - Regenerate
README.md
:rake build
(install Rake withbundle
) - Commit your changes:
git commit -am 'Add trick'
- Push to the branch:
git push origin my-new-trick
- Create new Pull Request and explain why your code is trick