I have been surprised by the fact that attribute aliases are not taken into account by commands.
For example, if I have in the schema:
attribute :foo, Types::String.meta(alias: :bar)
The input for my command is expected to be {foo: "Value"} instead of {bar: "Value"}.
In order to overcome this limitation, I have added something like this to my relation:
class MyRelation < ROM::Relation[:sql]
def self.renames
schema.each_with_object({}) do |attr, obj|
key = attr.key
name = attr.name
obj[key] = name unless key == name
end
#...
end
And then I have to create a custom changeset to be used for each command. For example:
class CreateMyRelation < ROM::Changeset::Create
map do
rename_keys MyRelation.renames
add_timestamps
end
end
As you can understand, this is a big PITA when you have lots of relations with aliased attributes
Would you consider adding it as default behaviour for rom commands? If so, I could help with a PR.
This is on purpose. The right place to put such logic is Changeset (like you did). This could be improved via a changeset plugin, unfortunately, we didn’t introduce a plugin API for changesets yet.
I think it feels a bit weird this asymmetry between reading and writing. I mean, when you define an alias attribute, relation methods like first or to_a (which read from the database) return tuples with the alias name as key. However, once you come back to the database with commands (which write to the database) that aliased name is no longer recognized. I think that a better approach would be to handle it in both sides automatically, so that the whole application layer can forget about the database name once the alias has been defined.
At least we could provide another pipe method like #map(:undo_aliases), but even so I think it is a leak in the abstraction from the database layer.
You can experiment with a command plugin and see how it affects the rest of the system. If it turns out this is an addition that doesn’t have a negative impact (read: doesn’t make things more complicated in other places), we could consider adding this to the set of core plugins.