7 ms·
Hmm, 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
by lloeki 3y ago
Hmm, 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.