Ruby's switch statement is flexible
akshaykhot.com
akshaykhot.com
For all its (sometimes deserved) reputation as a big and complex language, Ruby is actually pretty good at making sure that language features are made with the same underlying building blocks, and thus two things that look similar probably work similarly as well.
It's annoying when a library raises only one exception type for every error and the only difference is in the message. Matching on the message right in the rescue expression would be tremendously useful instead of parsing the message and deciding how to deal with it inside the rescue body.
It's a decade old and less than 100 lines so it might be more useful as an example than a gem. The last time I needed to do something like this I just rewrote the parts I needed. The ecosystem is just generally better about exceptions than it used to be, too.
It seems there's a swing back towards people considering raw productivity. Especially, at early stage or pre-product market fit.
Given that Rails is highly correlated with Ruby usage, it's simply hard to beat how much productivity and breadth that Rails gives you. Especially, with how much "grunt" work is easily handled by it.
I love trying new frameworks on side projects, but I always find myself coming back to Rails. There is inevitably some nuanced situation that the framework doesn't handle that would be ridiculously easy in Rails.
I don't even know if people realize that Ruby is the closest thing to an Aspect Oriented Programming experience in the wild, at least that I've seen. It's the secret sauce IMO.
Before that I was using Perl..
I even see Crystal (grammatically similar compiled language) gaining attention.
I do love Ruby syntax and used it on several occasions but it's typing system seems far from elegant compared to TypeScript in my opinion which doesn't want me to switch to Ruby at this point.
https://docs.ruby-lang.org/en/3.0/syntax/pattern_matching_rd...
This is where some real magic happens.
in [::Integer => data1, ::Integer => data2, ::Integer => data3, ::Array => data4] if (
data1.bit_length.<=(32) and data2.bit_length.<=(16) and data3.bit_length.<=(16) and (
data4.size.eql?(8) and data4.all?(&::Integer::method(:===)) and data4.max.bit_length.<=(8)
)
) thenI just learned this myself from another comment in this thread.
UPD: why do you write `obj.<=(16)` instead of `obj <= 16`? is this a performance thing or a matter of style or something else?
https://www.akshaykhot.com/ruby-switch-statement#pattern-mat...
The site presents evolutions of Ruby since version 2.0 in an editorialized and well-written categorized release journal called "Ruby Evolution": https://rubyreferences.github.io/rubychanges/evolution.html
There's also individual version releases annotated as well, for example for the recent Ruby 3.2: https://rubyreferences.github.io/rubychanges/3.2.html
Note that these are not copies of the NEWS.md typically released when minor and major versions of Ruby come out. Victor specifically spent time to write more descriptive notes of what each notable change occurred over time. It's an incredible resource and we're extremely lucky to have him in our community.
There's even a changelog for this meta-changelog, which makes my little Keep a Changelog heart sing, so you can see evolutions of this site over time as well: https://rubyreferences.github.io/rubychanges/
What features in particular are you looking at in Ruby 3.0? The type system stuff seems really cool, but I haven't been hearing much about it in the wild.
2. Hire a Pwn-As-A-Service to extract data from your company from the outside
3. Tell management "I told you so"
You didn't read this here
More seriously, I think the best you can do is not expect to find happiness in a setting that you don't fully control (ie your job), accept that, and look for joy elsewhere. It might be another job, it might be another hobby. Work fewer hours.
I've stopped looking for "joy" from work. Mainly focused on finding joy with my children and other hobbies, while working as little as possible. The job is the means to that end now, rather than the end itself. They continue to pay me and it supports the things I want to do outside of work so I am "happy" enough. The work itself is also moderately interesting and a decent challenge from a domain perspective, so it's not all bad.
I feel like the best you can do unironically is the first step I outlined: ask management to formally recognize that despite your warnings, they choose cost reduction. Have a trace of that, not just in case of problems, but for yourself: it's a great reminder that as long as you aren't in charge, it can't be your problem.
That's how I upgraded from Ruby 2.4 / Rails 5.0 to Ruby 3.2 / Rails 7.0
Afaik the upgrade has been reasonably smooth, and nothing like the ruby 1.9 days.
For us, it's just incredibly hard for us to justify the effort _and_ risk. Particularly, the risk.
Most of us have been burned by a major upgrade (either here or elsewhere) where things have broken in non-obvious, hard to test ways. This is particularly problematic in the dependency chain where support is limited. Breaking issues may turn us into unexpected maintainers of key libraries.
Management.
But then again, some other times management just sucks.
Edit: The maintenance isn't a long term view btw. They are not looking at a 5-6 year window where the migration cost slowly pays off. They are worried about optimizing the next quarter.
I can’t imagine working in a place that doesn’t want to listen to its engineers or experts. (The same works the other way around too: engineers not willing to understand the business context)
It's tiring when every line of code needs a value proposition and a clear use case. Some things don't take much work to improve/maintain but are next to impossible to slap a $ value on.
even = ->(x) { x % 2 == 0 }
even === 4 # true
even === 5 # false
That's cool. No need for a is_even or is_odd gem.I really do like that ability to do a case statement though. I cannot think of a situation where I need it now but I could definitely see it being handy instead of having to do if..else if
4.even? # true
5.even? # false
Or if you really want to use ===, it is possible with the slightly sillier (imho) use of Symbol#to_proc: case some_num
when :zero?.to_proc then "zero"
when :even?.to_proc then "even"
when :odd?.to_proc then "odd"
end
I say sillier, because even though it is very explicit, the use of #to_proc, by name, feels just a tiny bit less awesome than its usual use via the & operator, e.g. array_of_nums.map(&:odd?). It would be really cool to be able to write: case some_num
when &:zero? then "zero"
when &:even? then "even"
when &:odd? then "odd"
end
However I say this with no grasp of just how complex that could be to implement, or other implications for the language's grammar.It will also never match a number, although it's kinda ok because it's supposed to be an example of the functionality.
https://ruby-doc.org/3.2.2/ only seems to contain API docs.
The only official Ruby documentation exists at https://docs.ruby-lang.org/en/ and is generated via the Ruby source code itself.
The excellent Colby Swandale has been working on a new open source alternative for Ruby documentation considering the design shortcomings of the default generated API documentation with https://rubyapi.org which I highly recommend.
If you're coming at Ruby from another language, the official Ruby site offers https://www.ruby-lang.org/en/documentation/ruby-from-other-l... and other documentation aside from API concerns here: https://www.ruby-lang.org/en/documentation/
Hope that helps.
[1]: http://ruby-doc.com/docs/ProgrammingRuby/
Has nothing about the language changed since 1.6?
Looks like the 5th edition covers v3.2.
Fortunately (while there was a long time where this was not the case), there is a reasonably current version of the “Pickaxe” book, which is the closest existing thing to the documentation Ruby should have: https://pragprog.com/titles/ruby5/programming-ruby-3-2-5th-e...
There is an unofficial website with a better design, https://ruby-docs.org
“Switch” was never intuitive to me, because nothing gets switched.
select(a) {
when 1,2,3 {...}
when 4 {...}
when 5,6 {...}
otherwise {...}
} static String describe(int change) {
return switch (change) {
case 0 -> "unchanged";
case -1, 1 -> "mostly unchanged";
case Integer i && i > 0 -> "increased";
case Integer i -> "decreased";
}
}
No fallthrough, can mix constants and conditions (with syntax some will find clunky), and is an expression.In TypeScript === is more strict, and when I go back and forth between Ruby and TS I have made a mistake in the past by putting a === when I intended a strict equality check.
I wonder if there is a linting setting where you can prevent your code base from ever using it.
> Note: There're no break statements at the end of each when clause. Unlike other languages, Ruby's case doesn't fall through.
Aw no fall through?
> Note: Do not use obj.class in the case clause, as Integer === Integer returns false.
Wait what
irb(main):001:0> Integer === Integer
=> false
irb(main):002:0> String === String
=> false
irb(main):003:0> Object === Integer
=> true
irb(main):004:0> Object === String
=> true a = []
case a
when Array
puts true
else
puts false
end irb(main):009:0> String === String
=> false
irb(main):010:0> Integer === Integer
=> false
irb(main):011:0> Class === Integer
=> true
irb(main):012:0> Class === String
=> trueRuby's triple equals (`===`) is defined differently based on the type of the operand on the left hand side.
`a === b` is syntactic sugar for `a.===(b)`; and for classes `klass.===(other)` is defined as `other.is_a?(klass)`.
More examples here: https://dev.to/baweaver/understanding-ruby-triple-equals-2p9...
That's a good thing. In fact, that's the only thing on the article that is unambiguously good.
Fall through only serves to create bugs.
`Integer ===` (and more generally if `===` is implemented as a class method) checks if the right hand side is of the class Integer. The object Integer is not, it's an object of class Class.
EDIT: In other words:
$ pry
[1] pry(main)> Integer === Integer
=> false
[2] pry(main)> Class === Integer
=> true
[3] pry(main)> Integer.class
=> Classwhen 60, 70
SQL has CASE...WHEN which is a similar concept.
if (patternA.match(input))
elsif (patternB.match(input))
else
end
vs case input
when patternA
when patternB
else
end
And since it uses ===, you can define that on your own classes too. Hypothetically for instance, you can make fairly complex policy type classes to make reusable boolean statements. case current_user
when NotValidatedEmail
when NotCompletedOnboarding
when SomethingElse
else
end
and then you can use those elsewhere in the code by leveraging the more global use of === [user1, user2].any?(NotCompletedOnboarding)It's more of an implementation detail, but the Ruby VM actually does a jump if all the cases are "keyable"(typically integers, strings, symbols, etc). It does a hash lookup and jump to the returning address.
https://github.com/ruby/ruby/blob/92466e440d459cd21e89f8bfbe...
I should go read some of the interpreter code at some point - fun work going on there.
def can_drive(age) case age when 1..14 then 'no' when 15..100 then 'yes' end end
puts can_drive(18) # yes
I haven't used Ruby for a long time, and I don't feel bad about it.