[0]: A big disadvantage of the above is it's not statically resolvable, so you couldn't really build an IDE go-to-definition with it.
Solargraph is an amazing project but ctags is still just so fast and low overhead that it's what I mostly use.
ctag support is available for most editors and is built into Vim. You need to first generate a tag file (or have an editor plugin do it for you[3]) and then press Ctrl-] to go to definition.
With ruby 3.1 I think we’re getting a new default debug.rb gem.
It will be interesting to see if pry or the beat ideas from pry can be integrated with that.
THIS. 100%!
I also think Python seems to do this better with the namespace pattern of `import x.y.z`. Never seen the `import *` pattern before, didn't know Python can do that (never done Python really though either, only browsed some code here or there).
But one addition to your statement I'd like to add: not just idioms, but documentation can spread this antipattern too.
Noobs come to a language, read the documentation for some tool, library, etc., and understandably come away thinking "Oh ok, this is how it's done" when in reality, while they're not doing anything wrong per se, they're being taught a bad habit right off the bat. And that makes the inevitable hard slap across the face that reality (or a co-worker) eventually hits you with all the more shocking, possibly even leading to resentment of others when this happens.
I myself had this problem many years ago; "why the hell do you need all these subclasses and so many layers of abstraction?!" I once asked. "Because those class/method names already exist within other classes loaded at runtime, or will likely be added in future external dependencies, and we don't want to accidentally overwrite those."
Well, I thought that was a stupid idea. In my (very limited at the time) experience, when I had that problem, I just insisted on finding a damn good descriptive name that wasn't going to be used for anything else, even if it was wicked long or a pain to type. Too long? Don't be lazy. Typos more probable with length? Learn to copy/paste. That was the approach I learned at some point from some documentation somewhere on the internet, and it always worked for me, so it was the "one true path" and all others were inferior, making proponents or users/adherents of any other way "wrong" and inferior to me and my superior, non-lazy intellect. You're telling me that you do it differently? You must be stupid or lazy, and I'm neither therefore I'm better than you; thus forcing me to adopt your inferior standard?! Preposterous! Insulting! Offensive! That's how I saw it.
God, was I dumbass.
I developed this attitude by learning my skills in a near total vacuum, other than documentation written by people on the internet back in the 90's. Not official company-published technical docs, mind you, but forum and BBS posts, mailing list discussions, and way too much IRC to be considered "healthy" by any stretch of the imagination.
Those who taught me did so in a way that was efficient and they did no wrong at all of course - this failure was entirely of my own making, from my own arrogance. But it's also true that had I been taught at the same time that "there are other ways in other languages that are totally valid; this is just for simplicity's sake" and then been shown, or better yet required to use other patterns in school/projects/etc., I might have been able to see the short-sightedness of my own arrogance sooner. Instead, it resulted in an antipattern that allowed me to become overconfident, arrogant, and more difficult to work with than I should've been.
----------
In case you're wondering, I can't remember what I read or where that came from so long ago. Sorry. Might have been C, C++, Java, DOS BASIC (non-GUI/non-Windows), Visual Basic (before .NET) or maybe even PHP 3.something, no idea - that was 20+ years ago. I was inexperienced, arrogant, and a fool; none of that was the author's fault whatsoever. We're all young and stupid at some point in life. I'm no longer young, but at least now I know I'm stupid! :-)
Short of that, assuming all your dependencies are unpacked under your project's directory (likely .gitignore'd but there nonetheless, instead of in somewhere like /opt/ or /usr/local/lib or something) you could also `grep -ir 'def method_name' | less` or something to try to figure that out, though it's nowhere near as smooth as RubyMine.
Other editors have Ruby and/or Rails plugins that may help ease this pain point as well, though personally I haven't done anything with that on the editor front in a very, very long time. Generally I'm outright militant about keeping my dependencies to an absolute minimum in a project, and doing everything the most "vanilla" (standard, accepted, most widely adopted patterns, naming conventions, etc.) way possible, and therefore I usually know what gem/lib is providing what particular method, so I don't really need that kind of utility most of the time because I can google up some API docs...when there are API docs anyway.
But in cases where you're having a legacy project/monolith dropped in your lap and nobody knows anything because "the business guy" fired the one developer who actually knew how everything worked and now that guy won't answer his calls because working for free is dumb? Yeah, in those cases, RubyMine or a real good editor plugin can be a lifesaver for this.
People trying to write Java-like OO in Ruby end up confused and frustrated.
object.method(:name).source
object.method(:name).source_location
but frankly this is still thinking in a rigid mindset that suits other languages better. Ruby isn't just "dynamic dispatch"; a typical metaprogramming technique handles all incoming calls without named methods, or by dynamically writing the code.To put it bluntly, assuming there's a method on the other side of your message, is practically the antithesis of Ruby.
It's similar to the category error that leads folks to conflating type with class, and writing type checkers that look for class signatures.
Or if you mean, dynamically instantiated methods, those are found throughout Ruby and its ecosystem, and the reason it isn’t horrifying is that the language has terrific support at the REPL for developers needing reflection.
Well, since I have been privileged to work with several really great Ruby teams over the last couple of decades, whose large and well-structured code uses metaprogramming techniques yet remains easy to follow & comprehend (and debug and extend), and who remain splendidly productive to boot, I can say, if this bunch of guesses at the intended meaning of that "soap bubble" metaphor hold water, then yeah, I disagree.
Conversely, shitty code can be (and is) written in explicit import and early-bound languages. The enterprise world, for example, is riddled with massive hairballs of brittle, unmaintainable, boilerplate-heavy Java.
All of which only goes to show that people, not the programming language, are the problem.
Though having suffered in a Rails codebase written by Java engineers, I definitely agree that a light touch is needed with OO -- though composition has its own difficulties and together this forms one of my bigger criticisms of Rails.
RubyMine, and the "jump to definition" feature in particular, is the primary thing that enabled me to really "get" Ruby and finally understand how everything worked together.
If someone asks for directions you give them directions or a current map.
I understand using to find out why it's there, but not what it is.
Indeed, and the problem is that IDEs have encouraged developers to write convoluted code and to forget how to navigate a filesystem. Reading code and commit history is a much faster path to understanding a system than an IDE's autocomplete. I learned this from Ruby, and it serves me well in C, Kotlin, Scala, PL/SQL, Typescript, etc.
I would argue the exact opposite. Developers who force tools designed 30 years ago to dictate programming language best practices today are holding back the entire industry. There is no reason for my tooling to depend on my filesystem; imagine if my IDE integrated directly with vcs, and a language server provided both of those tools semantic information about my code, available on any device. Let's not kid ourselves either, utf-8 isn't human readable on disk, it's still a binary-like format.
Input and output parameters on a program with hundreds of thousands of lines becomes .. rough, to say the least. DryStruct has helped a lot; but several teams working on a single codebase with their own opinions of method calling patterns evolved over several generations all in a single codebase, and it’s … rough.
I think for people that come to love Ruby it feels more natural to think about a program that way as opposed to thinking about a purely lexical context of a piece of code.
That's also why I don't think it's a great idea to add types etc. to programming languages like python or ruby. They are not made for this and they should focus on what they do well. Use the right tool for the job, don't try to make every tool being able to be used for the same task.
In fact, the philosophy of convention over configuration et all makes Rails MORE scalable and easier to onboard new people than anything else out there that I’ve seen.
I recently worked on a Rails project for someone who didn't like to create scaffolds, models or controllers for small things, thinking that it would add to the bloat. But conceptually it makes everything 10x times easier and faster to find and understand
There are soooo many blogs about migrating away from Ruby, adopting Go, Scala, whatever statically typed thing.
There's a reason even Ruby is getting a proper static type system.
Its productivity is simply hard to beat.
Most of these companies are unicorns or public. https://spreecommerce.org/ruby-on-rails-most-popular-among-t...
I doubt they can be listed as a Rails success story.
Stripe doesn't use Rails either. That was a pretty bad article. And it is somehow from Spree.
Almost all the major former startups that I could find are from 2010 or earlier.
I've found that people that gravitate towards Ruby enjoy meta-programming. The running joke is that you're not a Ruby programmer until you've decided that you should write a DSL. Like everything, clever programming has its tradeoffs.
These days no one reads the source code of every method they call until something is not working the way they expected it to.
Personally, I find the JS ecosystem that absolute worst for this because of the amount of imported code to layer basic functionality on top of the standard library.
If you've worked with rails long enough - well at that point things like Devise are probably in your mental model of how they work.
Personally I still find the large unbridgeable chasm between Python 2 and 3 to be a large friction point in the ecosystem. But if I worked in Python a smuch as I worked in Ruby, I may find Ruby more troublesome.
The simple fact is all languages are getting better. The best tool is not always the one that would be best for the job, sometimes it's just the one you know best. And the tradeoffs we make for ease of programming and abstraction can then turn around and mean we are pushing megabytes of JS over the wire when we don't need to, and now we are trying to solve that with more explicit inclusion and automatic tree-shaking in build pipelines etc.
It's all good. Every tool has two edges. I'm much happier to see people continue to engage with this edges are figure out how to make them more effective than to always argue about which edge is sharper.
Memorization is not consistent between teams/team-members.
Memorization is also the fastest skillset folks lose if they step away from the language/toolset for a while (it degrades rapidly)
Worst of all - The RoR ecosystem (6 versions in) requires memorization about which set of things you need to have memorized for this specific version of the framework.
I'll take some boilerplate code EVERY FUCKING TIME. The magic becomes a curse.
I think in dynamic languages like Ruby and Python, there are two very different cases that we need to distinguish when thinking about this.
The first one is global references to modules/classes/functions. For example, in Python you may write:
import my_module
foo = my_module.Foo(some_arg)
So, if you wanted to find the definition of that my_module.Foo() reference, it's easy: go to the definition of my_module and search for the Foo class/function definition.In Ruby, you may see instead something like:
require 'my_module'
foo = MyModule::Foo.new(some_arg)
Which looks very similar to Python, with the difference that the `require` method doesn't assign any local binding, but just executes the file my_module, which in turn will modify the global namespace directly and make `MyModule` available at runtime. The `require 'my_module'` might not even be present on the file that's then using `MyModule`: it might be indirectly required by another module that this file uses. Or, in the case of using Rails or any framework with auto-imports, the file might not have any `require` calls at all!So, how is this resolved?
By convention, basically. If you see a reference like `MyModule::Foo` and want to see the definition of that, the convention is that that definition will live under my_module/foo.rb, so you can go to that file with some confidence that you'll find the the code for that class there.
So, if the convention is strong, and people follow it, this is basically a non-issue, and the go-to-definition can be done as easily as in Python, with or without an intelligent IDE.
---
The second case is when you have a dynamic reference to an object/function/whatever, like:
def do_something(arg):
arg.foobar()
In this case, neither Ruby nor Python (or any dynamic language in general) will help you much in trying to find the definition of the `foobar` method, as the type of the argument will only be known at runtime at the moment that this function will be called, and can even vary between calls.There are different ways to make this go-to-definition somewhat more ergonomic than a global search for `def foobar`. But on the language level, i think both Python and Ruby are basically the same in this case: not very helpful. We then compensate by using good naming conventions, type annotations/comments, or using powerful IDEs that analyze the code to make good guesses about who is calling what.
Without those imports, how will people reading the code find the definitions? How will they track down the source code for all the methods being called?
If the answer is "with an IDE / tools", then can't those same tools be used to solve the "mess of importing modules"? If you're using a good IDE, imports and go-to-definition are both solved problems, no matter what language you're using.
If you're not using an IDE, that's a more interesting situation, and without an IDE I much prefer having the explicit imports so that I at least know where to start digging.