Most active commenters
  • davetron5000(10)
  • (8)
  • stouset(5)
  • reactordev(3)
  • simultsop(3)
  • rorylaitila(3)
  • Lio(3)

193 points onnnon | 76 comments | | HN request time: 2.946s | source | bottom
1. freedomben ◴[] No.44502586[source]
Neat, this looks like it might be a good middle ground between Sinatra (which I adore) on the barebones/low-level end and Rails (which I also adore for apps that need it, but simple apps really don't).

Looking forward to trying it out!

replies(2): >>44504327 #>>44505920 #
2. sarchertech ◴[] No.44502604[source]
>What about monads or algebraic data types or currying or maybe having everything be a Proc because call?! You don’t have to understand any part of that question. But if you want your business logic to use functors, go for it. We won’t stop you.

I love this! It sounds exactly what I’d want to use for a side project or a project with a limited number of reasonable, senior coworkers.

3. wkirby ◴[] No.44502631[source]
I'm not trying to start a fight, but fwiw I adore both typescript and rails.
4. heeton ◴[] No.44502698[source]
Tried to login to the example site, unfortunately hit an error immediately after auth'ing with github.
replies(1): >>44503002 #
5. caseyohara ◴[] No.44502713[source]
> I know, we can vibe away all the boilerplate required for Rails apps. But how much fun is that? How much do you enjoy setting up RSpec, again, in your new Rails app? How tired are you of changing the “front end solution” every few years? And aren’t you just tired of debating where your business logic goes or if it’s OK to use HTTP DELETE (tunneled over a _method param in a POST) to archive a widget?

For your personal hobby project, go nuts. Break all the rules and cowboy all your own conventions.

But for business applications, the conventions Rails enforces at least make codebases somewhat familiar if you've seen a Rails codebase before.

At a certain codebase size, boilerplate is almost unavoidable. Unpleasant, but necessary. Personally, I'd rather have some conventions, rules, and guardrails for where the boilerplate lives rather than trying to navigate your homegrown pile of code. Good luck maintaining that spaghetti when you've got multiple developers.

It's not clear how this new web framework avoids boilerplate anyway, so I don't see how this is an improvement over Rails. Presumably you'll still need to set up lots of stuff yourself, like RSpec. If the framework sets all of that stuff up for you in a conventional way, then you're just back to square one as soon as you need to fight against the framework's conventions.

replies(3): >>44502836 #>>44503031 #>>44505426 #
6. ◴[] No.44502836[source]
7. ◴[] No.44502842[source]
8. davetron5000 ◴[] No.44503002[source]
Author here - the example doesn't allow logins now to avoid abuse. I wasn't 100% sure I'd make it public and just decided today to do it, so it's not yet ready for just anyone to login.

You can run the site locally, or view pages on the site by going to https://brutrb.com/adrs.html and clicking those links.

9. davetron5000 ◴[] No.44503031[source]
Author here. The framework does require setting stuff up. RSpec is not one of those things. It's the testing library you will use if you use this framework. I didn't create a lot of flexibility in the framework. For example, if you don't like RSpec, you will not like this framework :)

You may want to examine the docs more closely. There are plenty of conventions and very few that can be circumvented.

But day one of a new framework is not going to compete with Rails. Sorry!

replies(1): >>44503226 #
10. cosmojg ◴[] No.44503124[source]
Oh man, Brut's HIPPOCRATIC LICENSE[1] is interesting! Just excerpting bits of Section 3.1 and Section 3.2 for example:

  * 3.1. The Licensee SHALL NOT, whether directly or indirectly, through agents
   or assigns:
[...]

    * 3.1.12. Taliban: Be an individual or entity that:
      
      * 3.1.12.1. engages in any commercial transactions with the Taliban; or
      
      * 3.1.12.2. is a representative, agent, affiliate, successor, attorney, or
        assign of the Taliban;
   
    * 3.1.13. Myanmar: Be an individual or entity that:
      
      * 3.1.13.1. engages in any commercial transactions with the
        Myanmar/Burmese military junta; or
      
      * 3.1.13.2. is a representative, agent, affiliate, successor, attorney, or
        assign of the Myanmar/Burmese government;
   
    * 3.1.14. Xinjiang Uygur Autonomous Region: Be an individual or entity, or a
      representative, agent, affiliate, successor, attorney, or assign of any
      individual or entity, that does business in, purchases goods from, or
      otherwise benefits from goods produced in the Xinjiang Uygur Autonomous
      Region of China;
[...]

  * 3.2. The Licensee SHALL:

   * 3.2.1. Provide equal pay for equal work where the performance of such work
     requires equal skill, effort, and responsibility, and which are performed
     under similar working conditions, except where such payment is made
     pursuant to:
     
     * 3.2.1.1. A seniority system;
     
     * 3.2.1.2. A merit system;
     
     * 3.2.1.3. A system which measures earnings by quantity or quality of
       production; or
     
     * 3.2.1.4. A differential based on any other factor other than sex, gender,
       sexual orientation, race, ethnicity, nationality, religion, caste, age,
       medical disability or impairment, and/or any other like circumstances
       (See 29 U.S.C.A. § 206(d)(1); Article 23, United Nations Universal
       Declaration of Human Rights; Article 7, International Covenant on
       Economic, Social and Cultural Rights; Article 26, International Covenant
       on Civil and Political Rights); and
   
   * 3.2.2. Allow for reasonable limitation of working hours and periodic
     holidays with pay (See Article 24, United Nations Universal Declaration of
     Human Rights; Article 7, International Covenant on Economic, Social and
     Cultural Rights).
[1] https://firstdonoharm.dev/version/3/0/cl-eco-media-my-tal-xu...
replies(4): >>44503267 #>>44503474 #>>44503573 #>>44503812 #
11. stouset ◴[] No.44503226{3}[source]
To be honest, I love this framework (so far from what I've seen) but I do hate the choice of RSpec. I say this as someone who used RSpec for like a decade after it was first released. I "get" it. I have contributed code to it.

Minitest (which is bundled with Ruby these days) is Good Enough™. It doesn't require you to learn a new DSL. Everything is just Plain Old Ruby. The use of RSpec these days—IMO—is just cargo-culting forward what was the right decision from 10+ years ago. Having an pseudo-English style interface for testing isn't worth having the additional dependencies nor the mental overhead of needing to know how the RSpec syntax is actually mapped into Ruby concepts.

I'm not asking you to change it, you're welcome to have a different opinion than mine. But I am curious if you have strong reasons for requiring it. Particularly because MiniTest seems to be well-aligned with the rest of your design philosophies, unlike RSpec.

replies(1): >>44503274 #
12. wavemode ◴[] No.44503267[source]
All lovely ideals, but sadly means the project is not actually open source (by the official definition). And not compatible with the GPL and similar copyleft.
replies(1): >>44505660 #
13. davetron5000 ◴[] No.44503274{4}[source]
I thought hard about this decision. Every time I use MiniTest, I end up wanting a bit more that RSpec has and then switching to it. I also have been surprised over the years that the `expect(x).to eq(y)` seems to be relatively intuitive to people, despite the fact that it doesn't seem like it ought to be.
replies(1): >>44503314 #
14. stouset ◴[] No.44503314{5}[source]
Do you mind elaborating on what more you end up wishing you had?

Most of the additional features RSpec adds seem worthwhile but just end up being more complicated/confusing in the end, in my experience. Shared contexts/examples, for example. Others like let blocks should… just be methods.

I will say the change in… I think it was RSpec 3(?) to the expect(x) syntax was a hugely positive change, particularly in not having to monkeypatch the world.

replies(2): >>44503349 #>>44503911 #
15. davetron5000 ◴[] No.44503349{6}[source]
The biggest thing is the mocking system. MiniTest's feels so difficult to use.

I also like creating custom matchers vs. creating my own assert_* methods.

I would agree that many features of RSpec are, honestly, bad: shared examples, shared contexts, etc. Excessive use of let! and let, plus the predicate matchers are all just really confusing to me.

I actually thought about patching the RSpec gem to remove the features I didn't like :) Might still consider it heh

replies(2): >>44503530 #>>44507122 #
16. nine_k ◴[] No.44503456[source]
All frameworks are like that. They make it simple to solve a particular class of problems, by making choices for you, and providing code that supports these choices. If your problems fit the framework's shape, it can be immensely helpful. If not, go look for another. There's nothing wrong about building a framework that matches your particular needs and tastes.

If you need a universal toolbox, no framework will do, you need composable libraries. Most large projects end up facing this, but most projects are not large.

replies(1): >>44503787 #
17. mrsilencedogood ◴[] No.44503474[source]
People who try to enforce this stuff via license fundamentally misunderstand the nature of copyright law.

Do you plan to sue people to enforce your license? Do you think people who are committing war crimes / crimes against humanity are going to not violate your license alongside all their other crimes?

The only thing this license accomplishes is ensuring no even semi-serious business will touch this with a 10' pole because it's a completely bespoke license with no prior understanding by their legal counsel. But you can already basically do that via the AGPL, except that some companies who are well-meaning may actually still use it.

replies(1): >>44503554 #
18. stouset ◴[] No.44503530{7}[source]
The mocking thing actually touches on another point of frustration for me. I think the design of Rails ends up causing people to reach for mocking way too often in order to test things. At a glance I think Brut should avoid a lot of this by having things just be plain old Ruby objects.

I have dealt with countless Rails projects where testing things conventionally was difficult or impossible so mocks/stubs had to be used everywhere (controllers are the worst offenders here). When you start digging in to what's actually being tested, you find that the tests express little more than "this method is written the way it's currently written" rather than actually testing behavior.

Good tests should do three important things: catch bugs early in development, prevent regressions, and allow refactoring. Overly-mocked tests not only end up doing none of these but often actively work against these goals. They can't catch bugs because they reaffirm the current implementation rather than testing behavior. They can't catch regressions because any change to the code necessitates a change to the test. And they actively inhibit refactoring for both of those reasons.

All that is to say that maybe having a less-convenient mocking system is maybe a good thing :)

Also, since you're here, I want to say it also looks like your design encourages avoiding one of my other huge issues with Rails. I hate that ActiveRecord conflates the ORM layer with domain logic. This causes an antipattern where consumers of records (usually controller methods) pierce all the way down into the database layer by using AR methods and attributes directly. While convenient, this makes doing database-layer changes excruciating since your table layout is implicitly depended upon by pieces everywhere throughout the stack.

Better is to do what it looks like you suggest here: there should be an ORM layer that just exposes the database structure, and then you should layer domain objects on top of that which expose higher-level methods for interacting with persisted objects. If you change the database, you only need to change the mid-level layer. None of its consumers need to care that the underlying table layout changed.

From what I can tell so far I am very excited about Brut.

replies(2): >>44503590 #>>44504991 #
19. davetron5000 ◴[] No.44503554{3}[source]
I didn't come up with the license. You can read about it here: https://firstdonoharm.dev/

I didn't want the code to be All Rights Reserved, so I chose the best license I could find that communicates my desires - I assume that's what most people do when choosing a license?

I'm OK if a "semi serious business" don't want to use my software.

20. dmix ◴[] No.44503573[source]
Usually a signal of a personal/hobby project not to be taken seriously.
21. davetron5000 ◴[] No.44503590{8}[source]
Yeah, that makes sense. Where I end up wanting mocks is when this happens:

1 - build first version of feature, all core logic in a class I can test conventionally 2 - logic gets complex, test gets complex 3 - Eventually, I need to create some layering, where the class from step 1 now delegates to other classes. The initial test is more like an integration test and gets harder to keep up

At this point, there is a camp that says I should be using dependency injection and inject null objects for the dependencies. I get that idea. I am in the other camp that does not want to make custom objects just to satisfy a test. A mocking system can do that for me. So that's what I would do - mock the dependencies. The "real" versions would be tested conventionally.

I definitely do NOT just start with mocking imaginary internals though - I guess that's a whole other camp :)

22. reactordev ◴[] No.44503787{3}[source]
One can learn a lot from the Adapter pattern. Don’t force me into your box, provide a framework for me to solve my problems.
replies(1): >>44504039 #
23. ◴[] No.44503812[source]
24. ◴[] No.44503831[source]
25. dbalatero ◴[] No.44503911{6}[source]
Reading test failure output is much more readable and usable under RSpec vs. Minitest, to my eye.
replies(1): >>44507980 #
26. simultsop ◴[] No.44504007[source]
In an era of constant professional extinction threat, author took risk and motivation to build an entire framework for free. And there is you attacking their ego.
replies(1): >>44504658 #
27. simultsop ◴[] No.44504039{4}[source]
You should give some humility lessons...
replies(1): >>44504665 #
28. rorylaitila ◴[] No.44504177[source]
I like the emphasis on forms and pages. That's the approach I take in my apps. Forms and Links drive all interaction. Any JavaScript enhancements merely click an existing form (even if it's hidden). You can always inspect the HTML and know exactly the route that will handle the interaction. I think controllers are overused. It's really forms, models (backend) and views (Pages). A lot of the validation and ceremony of controllers can be handled more elegantly by the framework.
replies(1): >>44504402 #
29. davetron5000 ◴[] No.44504182[source]
I like Ruby! Ruby has types. They just aren't enforced by a compiler.
replies(1): >>44504219 #
30. gavmor ◴[] No.44504219{3}[source]
Well, yeah, then what's the point?
replies(2): >>44504291 #>>44508050 #
31. gavmor ◴[] No.44504239[source]
> Is this the index action of the widgets resource or the show action of the widget-list resource? is a question you will never have to ask yourself or your team. The widgets page is called WidgetsPage and available at /widgets.

Hm, uh, seems like now we will be forever asking ourselves this question about /widgets.

That being said, it looks good! Might get me back into ruby if I can come up with a project for it.

32. brightball ◴[] No.44504266[source]
Comfort, speed, preference, flexibility, lack of benefit on web projects where all data is by default a string, your ORM has already mapped the types directly in the database and will transform everything automatically without repeat definition, you're dealing with a lot of JSON API calls that may change where you don't _need_ all of the data so being forced into a situation of strictly typing 3rd party nested substructures can created a lot of wasted time...

Just off the top of my head. To each their own.

33. ◴[] No.44504287[source]
34. davetron5000 ◴[] No.44504291{4}[source]
I'm OK not using a compiler. Used one for years and am good on that for now. I like writing Ruby.
35. duck ◴[] No.44504327[source]
Another option is https://roda.jeremyevans.net/, which I've found is the sweet spot in smaller projects, but can still scale up. The plugin system works really well to extend and the routing tree is really easy to work with.
replies(1): >>44504845 #
36. MortyWaves ◴[] No.44504402[source]
Is there a reason you literally simulate a click event on the form instead of calling submit() on it?
replies(2): >>44504510 #>>44504528 #
37. hamzakc ◴[] No.44504430[source]
I see it is currently based on Sinatra. Just wondering if you have considered looking into Roda?
replies(2): >>44504716 #>>44508066 #
38. Mystery-Machine ◴[] No.44504510{3}[source]
`requestSubmit()`*

https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormEle...

39. rorylaitila ◴[] No.44504528{3}[source]
Yeah, it's escaping me at the moment all of the reasons, but calling .submit() on a form element doesn't exactly have all of the behaviors as calling .click() on a button element in a form, particularly multiple buttons in a form.
replies(1): >>44504579 #
40. hapidjus ◴[] No.44504579{4}[source]
Are you perhaps using the button name/value for something? Havent tested but i suspect that submit() does not pass that right?
replies(1): >>44504692 #
41. reactordev ◴[] No.44504658{3}[source]
AI is coming for us all, I’m not attacking, I’m simply stating the hubris in the statements pulled directly from their website.
replies(1): >>44508959 #
42. reactordev ◴[] No.44504665{5}[source]
Here’s one. “I wrote a web framework that bundles all my favorite tools and dependencies, maybe you’ll also like it” would be a great start.
43. rorylaitila ◴[] No.44504692{5}[source]
Yep that's one reason. I make heavy use of that.
44. davetron5000 ◴[] No.44504716[source]
(author here) It only uses Sinatra because I happened to know it and needed to bootstrap the low-level stuff. I don't know if it needs to be based on Sinatra in the long term - it should probably just use Rack.
replies(1): >>44506480 #
45. djfobbz ◴[] No.44504845{3}[source]
Roda is hands-down my favorite framework and my go-to no matter the size of the app. It's super lightweight, crazy fast, and a great alternative to Rails, without all the extra bloat. You still get everything you need to build solid apps, just in a much leaner package.

We recently moved a legacy Rails 2.3 app over to the latest Roda, and paired it with Vite, Stimulus, and Tailwind on the frontend...and honestly, we couldn't be happier with how it turned out!

46. raggi ◴[] No.44504950[source]
This reminds me of Camping without the dynamic parent class constructor.
47. hotpocket777 ◴[] No.44504991{8}[source]
_in your opinion_

My opinion is, _my unit tests_ are to protect my code against unwanted changes. To that end, unit tests test a single unit. And everything is mocked. If I have to rewrite a method, usually I rewrite all of its unit tests. Which is fine. They’re easy to write or rewrite.

Fully mocked unit tests are then supplemented with fewer “full stack” tests higher up the pyramid.

replies(2): >>44505051 #>>44510309 #
48. stouset ◴[] No.44505051{9}[source]
> My opinion is, _my unit tests_ are to protect my code against unwanted changes.

This is just about the most bizarre take on unit testing I've seen in my 25-year career.

> If I have to rewrite a method, usually I rewrite all of its unit tests.

If you have to rewrite your tests every time you rewrite a method, you are entirely defeating the point of testing. What value you get from tests that only assert that the current implementation is equal to the current implementation?

replies(2): >>44505282 #>>44505495 #
49. wrs ◴[] No.44505193[source]
Wow, that's quite a license. [0][1] Before and after adopting, be sure to check on the pay practices for the janitorial workers of the companies who mined the raw materials for the machinery that made the components in your laptop…

[0] https://brutrb.com/overview.html#brut-is-hippocratic-license...

[1] https://firstdonoharm.dev/version/3/0/cl-eco-media-my-tal-xu...

replies(3): >>44505290 #>>44505443 #>>44505606 #
50. hotpocket777 ◴[] No.44505282{10}[source]
> This is just about the most bizarre take on unit testing I've seen in my 25-year career.

Lol ok

> you are entirely defeating the point of testing

We will disagree here

> only assert that the current implementation is equal to the current implementation

I didn’t state that I do this

51. ◴[] No.44505290[source]
52. DoctorOW ◴[] No.44505443[source]
I would love to live in a world where this license was possible for the average person to comply with, but I'm not even sure an above average person could.
53. 1123581321 ◴[] No.44505495{10}[source]
You seem confused about what he was saying. I’m sure you are familiar with unit testing philosophy with your experience. Calling a function and expecting a response does test the behavior of the application, just at a lower level than a request- or multi-request level spec. When he says rewriting a method he is referring to changing the logic of it; a refactor leaves the tests unchanged. That shouldn’t have needed to be spelled out to such a senior developer.
replies(1): >>44506376 #
54. ◴[] No.44505606[source]
55. F3nd0 ◴[] No.44505660{3}[source]
And not free software, either. A shame. :/
56. entropie ◴[] No.44505920[source]
For me the middleground is hanami but ill watch bruts developement closely.
57. joewhale ◴[] No.44506237[source]
nice work!
58. stouset ◴[] No.44506376{11}[source]
Tests should not generally need to be rewritten unless you’re changing the externally-visible behavior API of a function or library (either the literal API, its effects, or its semantics).

If you’re changing the tests because you’ve changed the internal, non-externally-visible logic of your code, your tests are almost certainly providing you negative value.

Being able to refactor and re-run your existing test suite to ensure consistent behavior is possibly the important property of a good test suite.

59. naveed125 ◴[] No.44506443[source]
Starred it and don't even use Ruby (yet) This may force my hand to try it.
60. ◴[] No.44506480{3}[source]
61. lloeki ◴[] No.44507122{7}[source]
> I actually thought about patching the RSpec gem to remove the features I didn't like

Hmm it all sounds like you'd end up bastardising Rspec into being Minitest::Spec?

If you really like things like `expect().to` it's much easier to add it to Minitest::Spec (I did it back then, since there's `_().should` it's like 5-10 lines) than removing all those features and suffering the rspec weight.

62. Alifatisk ◴[] No.44507232[source]
Bruts own website was also interesting https://brutrb.com/overview.html
63. ilvez ◴[] No.44507396[source]
I understand they have this peculiar license, but does that means that we can't see the source code?
replies(2): >>44507649 #>>44508181 #
64. regularfry ◴[] No.44507649[source]
https://github.com/thirdtank/brut ?
65. plastic-enjoyer ◴[] No.44507760[source]
Another day, another web framework
66. Lio ◴[] No.44507980{7}[source]
I agree with this. RSpec may be an extra dependency vs MiniTest but with MiniTest I'll almost always need extra plugins anyway for things like reporting, colour coding, etc.

MiniTest is almost good enough but not quite there on its own.

67. Lio ◴[] No.44508050{4}[source]
You can use a static analysis tool to check Ruby types ahead of time (still not a compiler) or provide information for tooling.

Alternatively you use them at runtime to check the correctness of data, which I don't think you can usually do with say Typescript where the typing information is for the most part compiled away[1].

1. I may be out of date on this but when I last looked at runtimes that could take Typescript directly they just threw the typing away. You just didn't need to use the tsc compiler first.

68. ethan_smith ◴[] No.44508066[source]
Roda would be worth considering for its routing tree architecture which can be more performant and maintainable than Sinatra's linear routing approach, especially as the application grows.
69. Lio ◴[] No.44508113[source]
I loved David's book Sustainable Rails[1], it's one of the books on Rails I always recommend.

This is a really interesting take. I'd love see one of these small alternative frameworks full embrace Sorbet and use it for things like form validation. I guess that might defeat the gradual typing approach but it would definitely be interesting.

1. https://sustainable-rails.com/

replies(2): >>44509000 #>>44510389 #
70. jacob_a_dev ◴[] No.44508181[source]
agree a bit strange
71. simultsop ◴[] No.44508959{4}[source]
Ok, humans have sentimental approach to information, we are not machines as you imply. Similar statement in a launch of a volunteer project is considered attack.
72. krowek ◴[] No.44509000[source]
Very talented person, and good speaker as well. I recommend some of his talks as well, perhaps this one closer to the content of the book https://www.youtube.com/watch?v=CRboMkFdZfg.
replies(1): >>44509633 #
73. davetron5000 ◴[] No.44509633{3}[source]
Thanks for the kind words!
74. ansisjxbbx ◴[] No.44510309{9}[source]
This is domain specific, but for most web apps

> Fully mocked unit tests are then supplemented with fewer “full stack” tests higher up the pyramid.

Has the wrong priority. Full stack tests are the most valuable for shipping working software. Tests are enforcing contracts between components. Often I’ll see unit tests written for cases that do not exist. This is very bad. Testing at the boundary of your stack ensures the contracts that matter are enforced. Everything else is just making devs wonder “is this requirement actually real?”.

I used to be big on unit tests until I had to maintain a codebase for more than a couple years. I still use them, but mostly out of laziness.

75. FrancoisBosun ◴[] No.44510389[source]
Sorbet is nice, but for data structure enforcement and data validation, I much prefer the dry-rb ecosystem.