18 comments
cool…
learned something new..☺
Thanks for reading 🙂
Ah, the destructuring of block arguments is cool. I see you can do a.each {|(a)| # something with a}
and discard b, any way to discard the first argument? This throws an error as ruby tries to assign nil: a.each {|(nil, b)| # something with b}
.
Hey Ben,
you could try a.each { |(_, b)| # something with b }
, that’s the general convention for what you are trying to do.
Interesting, fun, maybe even useful. As a perpetual Ruby amateur (in the fullest sense of the word) I much appreciate and always read your posts.
Thanks for reading Tom 🙂
Awesome.
Looking forward to some metaprogramming tricks as well.
Number 9 is especially useful when it comes to feature flagging ruby/rails upgrades. You can wrap up deprecated methods and then push the code for testing.
Thank you so much for sharing, it is always good to discover the new !
I sometimes use the character literal for calls to split (e.g., "snake_cased_string".split(?_)
) or when building a string consisting of n times the same symbol (e.g., ?**5
for "*****"
) even though rubocop yells at me not to do this 🙂
Avdi Grimm shows an example where the redo keyword comes in handy in rubytapas episode 259 (only members can access it), but I’d agree that it is something that one usually does not need and even then there’s probably more idiomatic ways to go about it.
Thanks for your comment @kaikuchn. I have to agree with Rubocop on this one, but still interesting uses 🙂
For #8, ?a syntax, I find it more useful for intention documentation purposes. For example, using this in a comparison or assignment conveys that we are specifically interested in a single character. As well, you don’t incur the potential interpolation check overhead when doing like “a” vs ‘a’.
Indefinite indexing: this amused me, so here’s a similar one: indefinite calling ^^
public def call () () end ().().().().().().().().().().().()
Destructuring: There is also some limited support for nested destructuring
-> ((a, (b, c), d)) { a # => :a b # => :b c # => :c d # => :d }.([:a,[:b,:c],:d])
And you can destructure hashes in blocks, as well
-> (a:, b:1, c:2, **d) { a # => :a b # => :b c # => 2 d # => {:d1=>:d1, :d2=>:d2} }.(a: :a, b: :b, d1: :d1, d2: :d2)
Special Globals: There aren’t hard rules for these, they have a lot of options when they define them, so you won’t necessarily see consistent behaviour across them. Eg. $_
is scope local, but you can assign to it:
$_ = 'main' class A $_ # => nil $_ = "A" end $_ # => "main"
You can find their definitions by grepping MRI source for rb_define_hooked_variable
and rb_define_hooked_variable
.
Character Literals: My fav use is ??
as a place holder, eg I might write this, and then go back and figure out what those vars are
∆x = ?? ∆y = ?? Math.sqrt ∆x**2 + ∆y**2
Also, their existence is historical rather than practical, back when chars were still int literals (1.8, IIRC), it was useful to have a syntax for going from the char to the ordinal, which is what this was for. Then when chars became single-letter strings, they made the syntax resolve to that rather than deprecating it and breaking lots of code.
RbConfig Module: I totally didn’t realize about host_os
! That will be useful, ty!
Chaining the Shovel Operator: Note that this is b/c Array#<< returns self, if it didn’t, then this wouldn’t be true (compare to assignment, which always returns the assigned value)
class A def <<(*) 123 end def m=(*) 123 end end A.new << 456 # => 123 A.new.m = 456 # => 456
BEGIN & END
I think they come from awk, they’re useful on the command line when you use the -n
and -p
flags, eg 5 most common words in parse.y
cache curl -sL 'https://raw.githubusercontent.com/ruby/ruby/ruby_2_4/parse.y' | ruby -ne ' BEGIN { words = Hash.new 0 } END { words.sort_by { |w, n| -n }.take(5).each { |c, n| puts "#{c} #{n}" } } $_.scan(/\w+/).each { |w| words[w] += 1 } ' | column -t
Flip-Flop
Another command-line one, the best use cases for it have shortcuts, eg directly dropping $.
will match line numbers and dropping regexes into it will match the current line (this is actually a boolean thing, not a flip-flop thing). Sadly, ..
vs ...
chooses whether it will test the second condition on the first line rather than whether it will include the last line, which would have been more useful, IMO.
Eg: Print the environment variables section of Ruby’s man page:
$ man ruby | col -b | ruby -ne 'print if /^ENV/.../^\w/'
Thanks for your comments Josh! Very interesting additions 🙂
Number 5 does not work as you mention, it asigns 1 to ‘first’ and empty to ‘last’ and then 2 to ‘memo’ at least in my Ruby 2.3.0 on Mac
Sorry I made a mistake when copying the code from my notes. Should be working now 🙂
Re 1: Way back in the day people used DATA
sections (everything after __END__
) at the end of Ruby scripts for storing data. Here’s a blog post on this: http://blog.honeybadger.io/data-and-end-in-ruby/
Re 2: This works because ::
is used for scope resolution in Ruby: https://en.wikipedia.org/wiki/Scope_resolution_operator#Ruby
Re 6: The special variables become a lot less magic if you require 'English
: https://ruby-doc.org/stdlib-2.3.3/libdoc/English/rdoc/English.html
Re 8: I mainly use them in code golf 😉 Or when I want to be really explicit about expecting a one character string.
Re 13 and 14: both the BEGIN
and END
block as well as the flip-flop operator are remnants of Ruby’s Perl legacy. Though it’s been a long time since I actually saw them in code.
Now for some of my personal favorites:
*) Inheritance: the right-hand side of <
can be any valid Ruby expression as long as it resolves to a class.
class IdentityCrisis < [Array, Hash].sample ; end
*) Unary minus: to define a unary minus operation, you have to use -@
.
class String
alias_method :-@, :upcase
end
-"foo"
#=> "FOO
Also if you wanna confuse your friends, make them guess what this does: (1..5).map(&:-@)
😉
Infinity:
Before Float::INFINITY
was added in 1.9 we used to make our own: Inf = 1 / 0.0 #=> Infinity
Destructuring addendum: fairly obvious, but restructuring works for assignments as well:
first, second, _, *rest = [*1..10]
first
#=> 1
second
#=> 2
rest
#=> [4, 5, 6, 7, 8, 9, 10]
Thanks for your contribution Michael 🙂