5 ms·
Well, if you're using Ruby in the first place, you're probably not the kind of person who cares very much about how much the tooling can help you, because even
by emodendroket 3y ago
Well, if you're using Ruby in the first place, you're probably not the kind of person who cares very much about how much the tooling can help you, because even the best-in-class tools are primitive compared to what you get for other languages.
- psychoslave 3y agoCan you elaborate on this? Apart tools requiring static typing as a foundation, I am not sure what tools you are thinking of.
- Arnt 3y agoI agree. I work with ~5 of the top languages, don't see that difference, would like to hear an explanation. Even if I disagree with it in the end, I'll learn something.
- dtech 3y agoEven something as basic finding all references to a variable or function doesn't work very well in most IDEs. Shoutout to the folks behind Sorbet for making an awesome LSP for VSCode [1]. It comes close to the tooling expected nowadays, bit is still very far behind even popular dynamic languages like JS or Python. [1] https://sorbet.org/docs/vscode https://sorbet.org/docs/vscode
- emodendroket 3y agoTooling has gotten pretty sophisticated at working around that limitation for languages like JavaScript or PHP but Ruby lags way behind. The different between what RubyMine and PHPStorm or WebStorm can do is strikingly obvious to anyone who's worked with both. Or at least, if that's not the case anymore, that's how it was when I last compared the two environments.
- lloeki 3y agoHmm, as a Ruby dev my experience differs vastly. In a context where I'm working on a few languages regularly and among a larger set of languages that coworkers are in charge of, I can see how Ruby has solved a ton of problems that other languages are still struggling with. - package management: bundler/rubygems has been a sa-holved problem for the longest time whereas npm/yarn is an unholy mess, python is only beginning to see the light after refusing for so long, go mod is kind of a step in the right direction but still falls short in others, cargo is an outright copy but tries too hard to also be rake, a distinct problem making it a kintchensink... Gemfiles being descriptive, one can generate lockfiles from their platform including information for a foreign one and it's going to resolve consistently, including corner cases like this gem has a transitive dependency on a ruby extension and this platform has a binary gem at that version but that platform can build from source at that other version so I'm going to pick the one that's consistent and respects the constraints that have been described, or bail out and yell at you. This makes using semver-like squiggly a breeze and a non-event. - build tools: scons, cmake, ninja, doit & al. feel both incredibly complex and limited compared to rake. Every single time build files end up becoming obscure behemoths. When they don't they're incredibly focused on one thing but to make them to anything else they become a hodgepodge of additional external scripts and have to rely on so many tricks and hacks, which is why I used to stick to make. People seem to also have forgotten that rake can perfectly replace make as well, but rake can do so much more and still present a consistent interface and task implementation. - testing: minitest/test, minitest/spec, spec are running circles around any other test framework. The composability of reusable contexts, nested cases, and shared examples allows one to cover a ton of behaviours with very little test code that is eminently descriptive. Just the other day I did a deep reflector of some part of the code, and I only had to change a bunch of helpers as to how things should operate, and the entirety of my functional test suite was literally a zero-line diff. - talking to browsers: capybara is still my go-to to automate browsers, whether in tests or not, headless or not. It's just way too easy. - linting: Rubocop has a metric ton of tunable checks to enforce your preferred style and optionally auto fixes a lot of violations, standardrb takes a tab from gofmt by giving you some sane community defaults for Rubocop and thus autoformats by default. Rubocop is easily extensible, either you pull things and can lint rakefiles, Rails, spec and whatnot, or if you have special cases you can write your own cops. - typing and LSP: forget Sorbet, RBS+Steep is the way. RBS can generate typed skeletons either statically or dynamically using typeprof; ideally if you have good coverage you could run tests using typeprof and have automatic typing generation that can then be used entirely statically. Then the `rbs` command then allows you to statically ask a ton of questions about types but it's not a linter by itself, that's where Steep comes in. Steep comes with a LSP and it Just Works: autocompletion, inline type information, and so on. Type signatures can be packaged within gems, but if they're not there's rbs_collection, which also integrates with bundler. So I'm not too sure what you're referring to as "tooling" that would be primitive when compared to other languages when in my mind it's the other languages that constantly play catch-up ;)
- RangerScience 3y ago> testing Sounds like I should give !rspec a go... Since the rest of your comment was super informative, I'm interested in your take on RSpec and Cucumber? > RBS+Steep Hot damn I am out of date and this sounds amazing. Can't wait to try it out... Thank you for posting! > not too sure what you're referring to Best guess is it's like with patterns: Ruby doesn't have that many "patterns" (particularly the canonical kind) because they're just plain unnecessary. Similarly, thinking over other languages... there's just not much that's needed, which might then look like "not a lot".
- ncphillips 3y agoRspec/cucumber are great, but I’ve recently switched my app to minitest. It’s just as good for unit testing, and its system tests are a joy to work with. The setup and maintenance of rspec/cucumber isn’t worth it for me. It also makes it much easier for me to start new rails projects and get to my ideal workflow, because it’s the default workflow.
- calineczka 3y ago> Cucumber In every project that I've seen using it, it was a tremendous unnecessary waste of time in comparison to writing feature/acceptance tests just in Ruby. Gherkin is just such a poor language in terms of expressiveness compared to Ruby. Not to mention things like poor REPL experience when writing the scenarios.
- lloeki 3y ago> I'm interested in your take on RSpec and Cucumber? I found that Cucumber misses a bit on its promise: in theory things are perfectly described by high level behaviours but in practice it's a bit leaky and I regularly had to work around the tool, which was of disservice to readability. After all if you are faced with a high level descriptive test but have to know about some internals as to why this or that case has to be tested or why it has to be written this or that way (sometimes tacky ways) then it's probably that the tool is not quite fit. Rspec (or minitest/spec) eschews that: essentially it's just Ruby so you write what you need. The other side of the coin is that things you write might not be as "high level" as you initially want: you can have something working but very raw and possibly a tangled hard to maintain mess when it should be "higher level" (or several levels composed together). IOW the big advantage of Rspec is that it scales across from "lowest level" unit testing to "highest level" behaviour so it's always fit, but then it's on you to pick the "proper" level that fits and design your specs accordingly. An example (not perfect by any means but gives a good idea of composition), here we extracted a bunch of functional ("integrated") behaviours into separate examples: https://github.com/DataDog/dd-trace-rb/blob/57976d52bde573f9ea65cdba6a7fee6da290f13a/spec/datadog/appsec/contrib/support/integration/shared_examples.rb https://github.com/DataDog/dd-trace-rb/blob/57976d52bde573f9... Note that some are nested, and with a flick they test behaviour when e.g `appsec` is enabled or `tracing` is disabled. This creates a ton of combinations for only a few tests and coverage goes through the roof. Now here's how we use that: https://github.com/DataDog/dd-trace-rb/blob/57976d52bde573f9ea65cdba6a7fee6da290f13a/spec/datadog/appsec/contrib/rack/integration_test_spec.rb#L225 https://github.com/DataDog/dd-trace-rb/blob/57976d52bde573f9... Notice that magically every case is going to be tested and so we're absolutely sure that it works whatever a user toggles in their configuration. Notice that we create a Rack app, and then the tests proceed, just picking whatever app is in that `let :app`, which leads me to something we have yet to do: https://github.com/DataDog/dd-trace-rb/blob/57976d52bde573f9ea65cdba6a7fee6da290f13a/spec/datadog/appsec/contrib/rails/integration_test_spec.rb#L174 https://github.com/DataDog/dd-trace-rb/blob/57976d52bde573f9... https://github.com/DataDog/dd-trace-rb/blob/57976d52bde573f9ea65cdba6a7fee6da290f13a/spec/datadog/appsec/contrib/sinatra/integration_test_spec.rb#L178 https://github.com/DataDog/dd-trace-rb/blob/57976d52bde573f9... Notice how essentially just the `let :app` setup differs, but tests are entirely similar to the Rack ones (IIRC save for a couple that don't apply to Rack) so actually the overall behaviour can be entirely extracted and shared too! The result would be a per-framework test suite would be a) set up :app for that specific framework and b) a single `it_behaves_like "my super high level behaviour"` line, shared across all frameworks. Bonus (not sure you're familiar with it so here goes): this is tested using Rack::Test, which thanks to Rack's design provides a bunch of request methods (`get` and so on) that merely call the Rack stack as if it were a web server. IOW the whole app code is called just as if there were say Puma or Webrick in front but without ever creating a web server or a socket or whatnot, so it's stupidly fast, and since we're straight in the Ruby code we can mock or stub or allow/expect methods to be received or whatever is needed to our heart's content. Every bit conspires to produce a simple, readable, compact test suite that combinatorially covers every case and runs in a stupidly short amount of time.
- jeltz 3y agoI have no idea what you are referring to since Ruby has the second best tooling of all programming languages I have worked with after Rust. The excellent tooling is one of the reasons I liked working with Ruby, the language itself is pretty meh. A nice scripting language but nothing special.
- uticus 3y agoPlease, enlighten us
- zbentley 3y agoI'm not the OP, but I strongly agree with their statement. The problem is not Ruby's dynamic typing; table stakes here is the good-not-great tooling support for other dynamic languages like Python and JavaScript, not the great support for more typeful languages. I think Ruby plays really poorly with editor/static analysis tooling because of the culture and conventions around blocks. The fact that so many libraries/frameworks in Ruby use the very common pattern of "provide variables/context/symbols to blocks via scope (rather than block arguments)" massively increases the difficulty for tools to provide useful type info, completions, and validity analysis. As a pattern, "scope injection" is just harder to statically analyze from the outside, and, while not ubiquitous, it's alarmingly normalized in the Ruby community. I'm not sure if it's the chicken or the egg for many Ruby programmers' heavy reliance on the REPL for autocompletion and probes of local/instance/class/global variables. Regardless, editors' and linters' usefulness is reduced in proportion to the dominance of this pattern across the Ruby ecosystem. Heck, even when heavily block-oriented libraries are well supported by IDEs (rspec, rails) the support turns out to be based on brittle pattern matching and hardcoded guesswork in the tools. There are other cultural (e.g. tendency to overuse stringy method calls and method_missing, though this thankfully seems to be less common these days) and technical (e.g. autoloading, open classes) things that increase the difficulty of writing good Ruby tooling, but I suspect that they're much less responsible for the anemic tooling landscape than block-oriented programming. I really do think it's largely cultural. You can do block/scope-injection -based programming (and eval, and method_missing via getattribute, and so on) in e.g. Python without too much trouble, but it's not nearly as much of a community norm as it is in Ruby, so people do it less. And that's OK! Many prolific Ruby programmers have found ways around the tooling deficit and are productive with the language, and Ruby remains a go-to for people writing "DSL"s (though I find when people refer to DSLs in ruby they really mean the combined effect of parens-optional plus method_missing plus block scope injection), so this seems like far from a fatal flaw.