bglusman
IndiffererentAccess - Adaptation of HashWithIndifferentAccess to Elixir Maps/Plug
So I’m looking for reactions and alternatives, and I know a lot of them will be negative/some of mine are too…
I just threw together this code in an hour or two after frustration with a bug in my project where one dev used a string-keyed map in some business logic where some existing code assumed it could use an atom… and we have a number of similar situations elsewhere, where we fail to do anything to the params at the Router or Controller level and they leak, unchanged, into deeper business logic modules.
Arguably that’s the problem itself, and I know a lot of you (probably correctly) will think this is a terrible idea. I think it might be a terrible idea. But I also wanted to see how hard it was to do and have a discussion here over alternatives, tradeoffs, and whether it’s fundamentally bad in any important way or just non-idiomatic, and if, as I’m sure many will think, it’s non-idiomatic, what’s the right alternative that tolerates mistakes and oversights and doesn’t have a lot of ceremony or boilerplate?
One obvious issue with this is the overhead… another is that Map’s are enumerable and if you use this on a Map you enumerate over, well, things will be a bit weird by default. What other issues come to mind? Anyone willing to say they like it/try it out in a personal project and see how they feel after using it a tiny bit/pointing to specific places it works well or badly, aside from the general concerns over being idiomatic?
I lean towards being pluralist and even if I don’t use this myself going forward, I wanted to see what it looked like when done/how hard it was, and the only comparable libraries I found were GitHub - vic/indifferent: Elixir Indifferent access on maps/lists/tuples with custom key transforms. · GitHub and GitHub - philosodad/morphix: Tiny library of functions to perform transforms on maps and arrays. · GitHub and neither seemed to do quite as simply what I had in mind… and it was easy enough I thought i should share the result and see if I learn anything, or if people surprise me and actually like it ![]()
Anyway here’s the github GitHub - bglusman/indifferent_access: [ALPHA] - Elixir Plug/Utility doing questionable things with maps/params · GitHub and the hex package indifferent_access | Hex
Most Liked
LostKobrakai
The bug is passing string keys into business logic and not that the business logic expected atom keys, but you’re trying to change the latter instead of the first problem. User input (most often string keyed maps) should only ever land at a place where it’s Ecto.Changeset.cast or otherwise converted to an internal data structure. I would even say modifying those maps (e.g. add something to it) before “casting” to be a smell.
The other place for string keyed maps is if you need keys, which are user defined. But then your whole business logic needs to work with just string keys, because you never want to convert arbitrary user provided keys to atoms.
NobbZ
Exactly.
When I get input from the user, its usually that I validate it and typeconvert to elixir types as necessary. Then its either used for funcalls directly as bare value, or to create some other data. For what I have done so far, this other data is about 90% some struct that existed already before I decided to get some user input.
And in case of projects using ecto, well, at least 50% of them have a schema and a changeset function which makes validation/normalization very easy and straightforward.
Of course, sometimes other data structures make sense, then use it. And of course, if there are user provided relationships, just don’t use atom keys. But either way, deeper in your codebase nothing should access a map by a literal string, as either you know upfront what you expect or you don’t. And if you use a literal string, it feels as if you knew what you should get, but forgot to normalize. (And as always, there are exceptions to this “rule”, case by case)
NobbZ
I’ve only skimmed the implementation, but it seems as if it builds on a one-time creation of stringified/atomized counterkey with same value.
This seems to make things even worse…
One part of code updates atom key, another reads string key.
And even if you remember to re-key… Which should win if both exist?
The only solution are proper conversion to structs. No more arguing about string vs. atoms, just structs. Part of the contract, checked by dialyzer.
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance










