> Values in Hash literals and keyword arguments can be omitted. [Feature #14579]
> {x:, y:} is a syntax sugar of {x: x, y: y}.
> foo(x:, y:) is a syntax sugar of foo(x: x, y: y).
This is going to be controversial and take some getting used to, but I think eventually everyone will love it. It’s not an easy syntax to learn - I remember when this came to JS it took a while to get my head around all the different ways this shorthand was used in the wild.
It’s hard to come up with new syntax that can peacefully coexist with the established language. I think in hindsight it would have been helpful for Ruby if it had done something like square braces instead of brackets for hash literals, or not allowed brackets for blocks. The most ambiguous syntax all involves hash literals vs blocks… this adds to that cognitive load.
But typing out a long hash literal just to capture a bunch of local variables or methods is so common, and the duplication is quite annoying, so on the whole I’m glad this was added and am sure I’ll use it a lot :)
I'll throw myself out there to say that after being forced to read punning in JS for the past 2+ years, I've never come around to it. Sure it's quicker (and I'll admit even nicer) to write, but even after seeing it daily for so long, I still find myself doing double-takes on what I'm looking at, and I need as little as possible of that while reading code.
I'm a little disappointed that Ruby introduced it but no one is forcing me to use it so to each their own (and I'm not writing Ruby or much JS professionally anymore so it doesn't affect me too much).
This feature has such a positive effect on code quality in JS. It makes it easier to give consistent names to variables, and harder to use inconsistent names. You really notice when you write { foo: bar } instead of { foo } and it becomes somewhat of a code smell. Could variable bar be changed to foo? If yes then great, that's one less naming variation you have to remember for 'foo'. Across a whole codebase that can be a significant improvement.
>This is going to be controversial and take some getting used to, but I think eventually everyone will love it
It's saving a single character in your example. I can see liking it as solving a minor inconvenience. But love? I don't see that. To me it's just another thing to learn.
And the more items you add the more I find it useful. Yeah, it took some getting used to in Javascript after years and years without it, but I personally would not want to go back to not having it.
I'm not crazy about the colon/comma combo, but if I was writing in Ruby I suspect I'd get used to it after a while.
The curly braces in Ruby do an awful lot of work (blocks, procs, lambdas, hashes, interpolation) and I wonder if part of it is because it helped bring Smalltalk and Lisp-related concepts to people who were mostly familiar with C-style languages.
Arguably it was the right call considering where Ruby is now.
"The following default gems are now bundled gems." Great to see slow removal (or carve out) of rarely used stdlib stuff.
Ruby would benefit from a trimmed down stdlib, where effort could be spent keeping a smaller footprint stdlib more polished than it is today. Turning them into opt-in gems is a good first step.
A few examples:
- `rss` to parse RSS documents/streams
- `win32ole` for Microsoft Windows OLE Automation
- `dbm` for DBM databases
- `abbrev` for finding the shortest unique abbreviation amongst many strings
Enumerable alone is enough to outclass many stdlibs. Easily one of my fave things about Ruby, compared to dealing with different types of iterable interfaces (and conversions between them) in other languages.
Related: I believe there is ongoing work (not in 3.1) to modify the CRuby implementation to use shapes/hidden classes. This has significant potential to speed things up. More info:
https://chrisseaton.com/truffleruby/rubykaigi21/
No news about Ractors! The new concurrency facilities that Ruby 3.0 introduced. Anyone has an insight if there was any further development on that front?
"By default, all C extensions are recognized as Ractor-unsafe. If C extension becomes Ractor-safe, the extension should call `rb_ext_ractor_safe(true)` at the `Init_` function and all defined method marked as Ractor-safe. Ractor-unsafe C-methods only been called from main-ractor. If non-main ractor calls it, then `Ractor::UnsafeError` is raised."
I've submitted a few such patches for my own personal use, and it's a very trivial change for extensions which keep no state in C-land that would need to be synchronized between Ractors, e.g. https://github.com/dearblue/ruby-extattr/pull/1
I believe Stripe and Shopify only use RBI in production since Stripe develops Sorbet [0] and Shopify develops Tapioca [1]. Both tools work exclusively with RBI files instead of RBS files afaik. I haven't heard of anyone using RBS in production.
I'm surprised there doesn't seem to be more interest in type-checking in the Rails community at large (maybe I'm wrong). TypeScript has been make-or-break when choosing a company I apply for.
Ruby's extremely rapid iteration cycle (e.g. by embedding pry for debugging and experimentation), good test coverage, and fast search tools like ripgrep and fzf alleviate a lot of the pain you might get from the lack of static types. There are also gems for schema validation that predate Ruby's type annotations and help with runtime type checking on data coming from the wire or disk.
What a coincidence, I just started learning a bit of Ruby with an interpreter I'm writing a couple of nights ago.
I will take the opportunity to ask; does anyone have a good introduction to creating Ruby (not Rails) projects? I'm a bit confused between rake, bundle, testing frameworks, how to debug a program or structure a project and I can't seem to find a good up to date resource on that.
I don’t know a great “how to lay out” guide off-hand, but I’ll take a stab at helping with the confusion.
Rake is a Ruby equivalent of Make. Many Ruby gems will include rake tasks (Makefile targets) that can be used to drive pieces of an application. Most of the time your test framework of choice (rspec typically) will include “rake test” as a target for running the tests.
The tests usually live in a folder named “spec”, while application code lives in “lib” and your driver code lives in “bin” or just a top-level file… without some of the Rails initialization glue, you’ll have to do a little work to require files via relative paths or set up the $LOAD_PATH variable, etc.
Bundler is the package manager, and you’ll often see commands like “bundle exec rake”, this is similar in spirit to a virtualenv. Bundler will use the libraries defined in your Gemfile to run the given program (in this case, ‘rake’). The command “bundle” will read the Gemfile and resolve dependencies to concrete versions of libraries and write out a Gemfile.lock for you with all the versions explicitly defined.
For debugging, there are a number of tools out there, but I’m partial to ‘pry’. You add pry to your Gemfile, require it, and then can add a breakpoint by putting a call to “binding.pry” in your source code wherever. This will drop you into a REPL within the context of the program at that point.
pry is useful, but there are some other gems and settings files out there that can make it a little friendlier, pry-byebug comes to mind as having saner keyboard shortcuts iirc.
If you need help with overall file system layout, etc, the best place to look is going to be existing trivial gems on rubygems.org. Granted these will have a couple of minor differences from a “plain” application because they’re packaging as a library (*.gemspec file instead of Gemfile, and usually has a version.rb file somewhere).
Thanks for the awesome reply! This has been really useful, I was mostly confused about bundle/rake now I understand most of the things to through rake but bundle just provides the env, makes sense.
Also I created a project with bundle, which gave me some structure, and downloaded Rubymine which is helping a lot as well, even if it's a tiny project, also it suggests me new cool things Ruby can do which I had not idea about.
Bundler has a nice subcommands for setting up a new gem. Setting up a test framework depends on which one you pick. The big switch from rails is that you get to decide where everything goes. I suggest looking at some recently created gems by prolific rubyists to get a feel for the structure.
I agree with @tinco. If you're doing Ruby development that is not Rails, you probably want to create a gem with Bundler. I'd check out their documentation on the subject which has some good information: https://bundler.io/guides/creating_gem.html. I'd also checkout https://www.learnenough.com/ruby. I haven't walked through the course, but Michael Hartl has been providing Ruby related learning materials for a long time and is a fairly trusted name in the community. I'm sure it's solid.
If you want to make a cli application with ruby i'd suggest thor or dry-cli as a gem(package in other languages).
rake is ruby make... aka a build utility but it can and is used for a variety of things like testing and building and distribution.
minitest is built in and is generally enough. rspec is the other big one and a lot of people like it but it's fairly complicated and it is better to stick to the basic one early on.
as for debugging puts debugging is common(though a lot of things are caught at the testing phase since testing is big with ruby). if you are familiar with debuggers that is also an option with something like vscode or the command line but vscode is more usable(or the rubymine one that is a paid app).
`bundle gem foo` will create a subdirectory `foo` and setup a decent starting skeleton for a gem named 'foo'. I'd maybe suggest 'standard' over 'rubocop' as a linter to begin with.
foo/
- foo.gemspec: needs to fix the TODOs to start, and this is where runtime deps will go
- Gemfile: where your development deps will go
- Gemfile.lock: built by 'bundle install' do not touch
- Rakefile: the build system (build gems, push gems, run tests, etc)
- bin/ drop binary scripts here or delete if you're making a library (generally make them tiny stubs that require "foo" or "foo/application" and call run on the app or something)
- lib/ this is where you put all your code. already setup with lib/foo.rb and lib/foo/version.rb which are accessible via `require "foo"` and `require "foo/version"` mostly you'll want to put code under lib/foo/ and then have `lib/foo.rb` be a pile of require statements (ish).
- spec/ this is where tests go. bundler will set you up with rspec installed to begin with with a spec_helper.rb already setup (this is where you tweak rspec). i would suggest using spec/unit/whatever_spec.rb to test lib/foo/whatever.rb and keeping those directory structures roughly in sync for your unit tests.
To get started:
bundle install # updates all the gems in the Gemfile (plus the referenced gemspec file) and installs rspec and updated rake and stuff
bundle exec rake spec # runs all the spec/ tests
bundle exec rake # runs rake + rubocop/standard both
bundle exec rake -T # shows all the rake tasks
bundle exec rake release # will build and push the gem to rubygems publicly
I really can't wait to use the IRB Autocomplete feature. Aside from the obvious advantage of tabbing (convenience, fewer typos etc), it's a subtle way to improve discoverability of available methods. Very cool.
How good is `debug.gem`? I'd be excited to try it if I was still using ruby ...
I'm not using ruby anymore but the debugger experience I had from vscode via the rdebug-ide library a few years ago was _mostly pretty_ nice aside from very performance limitations ... a massive performance improvement on that front must feel great!
Sorbet[0] is probably the closest to what you're looking for. It's not something I would recommend to someone who wants to get started with Ruby, since it would be limiting to learn it as if it was a statically typed language.
Yeah I've tried setting it up for a library but Sorbet won't pick it up, Tapioca won't pick it up. It'll generate its own types which are exactly what I don't want.
Beyond that, it's too intrusive in the code for me. I could live with 'sig' blocks above methods, but there's an immediate readability cost when you start having to wrap stuff in T.let, T.must, T.nilable, etc.
Personally I'd rather improve my testing skills here too. Knowing how to write a good test is a valuable skill.
The main thing that’s different in ruby is the support for and widespread use of metaprogramming.
I’d suggest using metaprogramming (define_method, instance_variable_send, instance_eval, send) to implement a toy ruby-in-ruby DSL is the fastest way to figure out what different.
no exactly the same as js->ts but Crystal language in a typed, compiled lang. with Ruby like syntax but other nice features like Go like concurrency & speed, macros, great C interop. and they are working on an interpreter, too.
>where every developer in the company was handed a new M1 this month
That's pretty interesting. They deploy, I assume to jitted ruby on X64 boxes, but will develop on M1 Macbooks. I suppose they probably have some kind of automated performance/crash/leak/etc regression tests in the pipeline?
I say it in the third person, but I actually joined Shopify in October.
Development can be done locally, or it can be done via on-demand containers in the cloud with the IDE loading the code over a remote mount of the containers filesystem. I'm new to it all, but it's a very slick setup.
Between Ruby and Python, Ruby used to be deemed the slower language. With all recent improvements from the 3x3 initiative and yjit I wonder, does Ruby officially surpassed Python on performance?
> Values in Hash literals and keyword arguments can be omitted. [Feature #14579]
> {x:, y:} is a syntax sugar of {x: x, y: y}.
> foo(x:, y:) is a syntax sugar of foo(x: x, y: y).
This is going to be controversial and take some getting used to, but I think eventually everyone will love it. It’s not an easy syntax to learn - I remember when this came to JS it took a while to get my head around all the different ways this shorthand was used in the wild.
It’s hard to come up with new syntax that can peacefully coexist with the established language. I think in hindsight it would have been helpful for Ruby if it had done something like square braces instead of brackets for hash literals, or not allowed brackets for blocks. The most ambiguous syntax all involves hash literals vs blocks… this adds to that cognitive load.
But typing out a long hash literal just to capture a bunch of local variables or methods is so common, and the duplication is quite annoying, so on the whole I’m glad this was added and am sure I’ll use it a lot :)