Aliased attributes in commands


#1

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

Would you consider adding it as default behaviour for rom commands? If so, I could help with a PR.

Thanks


#2

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.


#3

Thanks for your response @solnic!

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.


#4

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.