bglusman

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 :upside_down_face:

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

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

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

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.

Where Next?

Popular in Announcing Top

mspanc
I am pleased to announce an initial release of the Membrane Framework - an Elixir-based framework with special focus on processing multim...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story <- Meeseeks.all(html, css("tr.athing")) do...
New
josevalim
Hi everyone, We would like to announce that Plataformatec is working on a new MySQL driver called MyXQL. Our goal is to eventually integ...
New
restlessronin
The repo is at GitHub - cyberchitta/openai_ex: Community maintained Elixir library for OpenAI API · GitHub. Docs are at OpenaiEx User Gu...
152 10342 134
New
woutdp
Hi! I wanted to introduce my latest project LiveSvelte. It allows you to render Svelte inside LiveView with end-to-end reactivity. It’s ...
New
Crowdhailer
Experimenting with this code. OK.try do user <- fetch_user(1) cart <- fetch_cart(1) order = checkout(cart, user) save_orde...
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
Hal9000
Here is my first stab at this. README pasted below. https://github.com/Hal9000/elixir_random Comments and critiques are welcome. Thank...
New
Qqwy
TypeCheck: Fast and flexible runtime type-checking for your Elixir projects. Core ideas Type- and function specifications are const...
336 14482 100
New
marcuslankenau
I feel kind of stuck with the absence of a proper xml library for Elixir. Currently I use SweetXML which was ok for me more or less to pa...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36352 110
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement