A Replacement for Strong Parameters
ryanbigg.com
ryanbigg.com
Strong Params requires you to enumerate all params on the server. But why couldn't you know what are valid params while building your form? You could serialize the params shape in the form builder and include it in your post payload, signed by the app secret to avoid tampering.
It should have done this from the beginning!
If I know, for instance, an admin is identified by an `is_admin` flag, I could add this to the allowed params and send my flag. Bam. I’m now an admin.
I know the example is a bit simplistic, but it wouldn’t take long to comb your app for a real world example where this would be problematic.
I’m not super keen myself as I prefer the implementation to be much more explicit than magic, but there doesn’t seem to be an obvious security hole here.
Strong Params is a canonical violation of DRY. When you build your form, you fill it with fields that are allowed to be filled out and submitted. Then you have to duplicate that work in the controller for no obvious benefit.
As for "more explicit than magic" I prefer to think of this as "automatic" or "conventional" rather than magic. Much of Rails' "magic" is this exact kind of thing, allowing the right thing to automatically happen with the ability to step in or override manually when the convention isn't what you want.
I get why you'd want it to be more explicit. I think that this feature was invented to solve a security problem and the solution was to give devs more work to do. There's no obvious reason why it couldn't be automatically taken care of for the dev and it would definitely make maintenance and legacy app upgrades smoother.
Besides, you still have to validate the signature server-side, so it's not like it's saving any work. Validation just gets split up between generating the signature, a network round-trip, and validating the signature.
The "work" the GP is trying to save here isn't CPU cycles, but rather developer labor — the redundant labor of writing both a form view that describes form inputs, and a form-value schema validator to be called from the controller that receives the form's submitted input values.
Generating the signature, and validating the signature, would both be done transparently by middleware components of the framework, with no marginal developer labor required per form.
It would probably be not too hard to do as a gem - you inject a hidden field when form_with is called and you define a new method on params so the controller can indicate that this approach is being used.
I can see how you could generate the Extract step of an ETL process (i.e. a validation specification) purely from the form HTML itself. But I'm not sure where you'd stuff the information required to do the Transform or Load steps. It isn't cleanly separable into per-component data attributes.
I think the pattern is helpful but it’s still pretty easy to mess up.
Aside from that, I don't love dry libs either, they are often very complex for their usecases and they all have DSLs that could have been better served by using ruby, datastructures and objects
He doesn’t work for us anymore (not because of this)
dry_schema looks nice and a certain improvement over strong params, but I'd go a step further.
Ryan is a smart guy, but it seems like he’d be a lot happier with something that isn’t Rails.
I just use the right tool for the job, and in this place I do not think strong parameters is the right tool for the job.
Indeed, Rails pays the bills.
And pulling out companies from the trouble they are in due to common Rails misuse is still very rewarding.
Haven't yet figured out how to help companies deep into callback hell though. Might be non-solvable.
The dry-rb approach isn't for me. That doesn't mean it's not going to resonate with a lot of others. (Clearly, it does)
To me, the Dry Schema approach feels unnecessarily heavy-handed and duplicates work that I would expect to be done in the model. Strong Parameters helps a bit with form data safety, but I'm not looking for anything else out of it. I'm expecting my models to validate and coerce those strings into something useful, and raise errors if the data is invalid. Dry Schema feels like an unnecessary layer in the middle. ¯\_(ツ)_/¯
A unique schema for each route that is strongly typed which can be turned into a specialized streaming JSON parser.
Given Rails is heavily dynamic, it reaches lawlessness and with no regards to data integrity, eventually leading me to decide to exit ruby and rails altogether.
With Ruby (& Rails) every single parameter is suspect and cannot be trusted. Stripe's type checker does alleviate some of this stress and anxiety.
The mistake a lot of people make: Rails itself isn't domain code.
In hexagonal-architecture terms, Rails is a framework for building your "web gateway", not for building your domain logic. Your domain logic should live on its own, outside Rails, as plain-old code with a well-defined API (consisting of functions and domain types.)
Rails' job — like any other hexagonal-architecture gateway's job — is to translate in incoming requests into command or query objects to pass to the domain-logic API; and to translate the domain-logic API's generated events or query-result objects back out. The former is exactly an MVC controller; the latter is exactly an MVC view.
Think of it like building a (portable, multiplatform) game: your business-logic is like the game engine, assets, scripting, etc. Rails, meanwhile, is like the OS-specific glue code that makes your game run in a window and respond to mouse/keyboard input on Windows/macOS/Linux etc. This glue code takes events from each OS and brings them into your game; takes output from your game and feeds it back to OS drivers; and binds OS mechanisms like window controls to game features. You don't want any "game" logic to live inside your OS-specific glue code. You want your glue code to just be a "wrapper" that brings in your game as, essentially, a library, and then "wires it up" to the given OS.
In this view, both Strong Parameters and dry-schema are tools used "at parse time" — i.e. controller-execution time within the web gateway — to create the "strongly typed" "unique schema" for the route, that allows it to discard/reject data before putting it into the real strongly-typed objects: the business-domain objects used by your business-logic DDD context.
(And the reason that this gluing-together is done through an arbitrarily-programmable framework, rather than by just giving routes static strong types, is because the translation process itself — especially when compatibility with multiple legacy systems is involved — can be "Turing-hard", requiring a full programming language to specify what should be glued to what, and when it's valid to translate X to Y vs X to Z. Maybe some users are talking to v1.1 of an endpoint while some users are talking to v1.0, based on a per-user feature-flag or holdback-flag or whether the user is a paying customer or not.)