Looking for some cool Ruby tricks?
You found them!
In this article I want to share with you some of my favorites.
Contents
Deep copy
When you copy
an object that contains other objects, like an Array
, only a reference to these objects is copied.
You can see that in action here:
food = %w( bread milk orange ) food.map(&:object_id) # [35401044, 35401020, 35400996] food.clone.map(&:object_id) # [35401044, 35401020, 35400996]
Using the Marshal
class, which is normally used for serialization, you can create a ‘deep copy’ of an object.
def deep_copy(obj) Marshal.load(Marshal.dump(obj)) end
The results:
deep_copy(food).map(&:object_id) # [42975648, 42975624, 42975612]
Different ways to call a lambda
my_lambda = -> { puts 'Hello' } my_lambda.call my_lambda[] my_lambda.() my_lambda.===
If possible, you should stick with the first one (call
), because it’s the one most people know.
Creating a pre-filled array
The Array class can take an argument + a block, which lets you create an array with n
elements. By default these elements are nil
, but if you have a block, the values will come from it.
Example:
Array.new(10) { rand 300 }
This will generate an array with 10 random numbers which are between 0 and 299.
True, false and nil are objects
true.class # TrueClass false.class # FalseClass nil.class # NilClass
There is only one copy of these objects, and you can’t create more even if you wanted.
This is the singleton pattern in action.
Lambdas are strict about arguments, but Procs don’t care
my_lambda = ->(a, b) { a + b } my_proc = Proc.new { |a, b| a + b } my_lambda.call(2) # ArgumentError: wrong number of arguments (1 for 2) my_proc.call(2) # TypeError: nil can't be coerced into Fixnum
Execute code directly without irb or files
The ruby
command has a number of interesting options you can use.
For example, with the -e
flag you can pass in a snippet of code to be executed.
ruby -e '5.times { puts "Fun with Ruby" }'
You can find more by using the -h
flag.
Your own mini-irb in one command
Ever wanted to know how irb
works? Well, this is a super-simple version of it.
Remember what ‘REPL’ stands for: Read-Eval-Print Loop.
ruby -n -e 'p eval($_)'
You won’t get a prompt, but go ahead and type some Ruby code.
"A" * 5 "AAAAA"
This works because the -n
flag does this:
-n assume 'while gets(); ... end' loop around your script
And $_
is a global variable. Which contains the following:
The last input line of string by gets or readline.
Unfreeze an object (danger!)
There isn’t any Ruby method to unfreeze an object, but using the Fiddle
class you can reach into Ruby internals to make it happen.
require 'fiddle' str = 'water'.freeze str.frozen? # true memory_address = str.object_id * 2 Fiddle::Pointer.new(memory_address)[1] &= ~8 str.frozen? # false
Don’t try this at home!
Objects with special identity
Ruby objects have an identifier or ‘id’ number you can access using the object_id
method. Some objects have a fixed id: Fixnums, true, false & nil.
false.object_id # 0 true.object_id # 2 nil.object_id # 4 1.object_id # 3 2.object_id # 5
Fixnum ids use this formula: (number * 2) + 1.
Bonus: The maximum Fixnum is 1073741823
, after that you get a Bignum object.
Avoid big output in irb or pry
If you are working in irb
and want to avoid filling your screen with the contents of some really big array or string you can just append ;
at the end of your code.
Example:
require 'rest-client' RestClient.get('www.rubyguides.com');
Try again without the ;
to see the difference π
Using the caller method to get the current call stack
Here is a code example:
def foo bar end def bar puts caller end foo
Output:
-:3:in 'foo' -:10:in '<main>'
If you need the current method name you can use __method__
or __callee__
.
Bonus! Convert any value into a boolean
!!(1) # true !!(nil) # false
Bonus! Use A Keyword As A Variable Name
def foo (if: nil) binding.local_variable_get(:if) end foo(if: true)
Summary
I hope you enjoyed these Ruby tricks!
Share them with you friends so they can enjoy them too & subscribe to my blog in the form below so you won’t miss my next post. π
Thanks, Jesus!
I like your blog π
Just want to add a little detail to trick 10: I think that just putting ‘;’ after any ruby code will suppress the output.
RestClient.get('blackbytes.info');
You are right! Thanks for pointing that out π
But in (my) irb, just leaving a trailing
;
will not execute the expression. It is now waiting for another expression on a new line. I usually append ‘;nil’.Maybe there is another dependency (readline?) in play.
Thank you so much, Jesus, it’s really kind of you to share such useful tricks! Cheers
Thank you. I’m glad you found them useful π
Good article.There are some tricks i have not seen before.
Thanks for reading π
Good Tricks
Good one, thanks for posting π
Thanks for reading!
My inner troll politely asks, “What happens when you deep clone objects which are actually just wrappers over external resources, i.e. sockets, file handles, db connections, etc.?”
That’s an interesting question π
I just ran a quick test and the answer is simple: Ruby won’t let you do it.
I tried with a socket and I get:
TypeError: can't dump TCPSocket
.Adding “;” to the end of the line will also let you create a function on a single line. IE: def hello_world; puts “Hi”; end
For #1, for a simple string array, we could also use the following.
food.map(&:clone).map(&:object_id)
May be, if the objects are deep by multiple levels, marshaling could be a way.
Calling a lambda with triple equal doesn’t make a whole lot of sense to me π
This is a thing because case statements use
===
.You can read more about that in this post.
Trick #3 incorrectly states “This will generate an array with 10 random numbers which are between 0 and 300.”
Actually rand(max) generates a number less than max: 0 <= n < max
http://ruby-doc.org/core-2.3.0_preview1/Random.html#method-i-rand
You’re right. I have corrected the problem, thanks for letting us know.
Here’s another one for your list… a little gem which uses Fiddle to access an otherwise internal-only part of Ruby itself to access the bindings of any caller of a function. If a calls b, which calls c, which calls d… then the ‘bindings’ gem will allow the d method to access variables defined in c, b, or a. The code is tiny and has been extremely helpful for us!
Check it out at: https://github.com/shreeve/bindings
Thank you π
“Bonus: The maximum Fixnum is 1073741823, after that you get a Bignum object.”
Only for 32-bit systems.