Something I've run into often is that validation errors can't be fully compressed into a single code. A common enough case is validating length:
assert_length :username, 3..16
If the user's provided username was out of bounds, we'd get a { foo: [:not_in_range] } error. Now, the error message we want to render will most likely need to include the range in which it is valid. Something like "The username must be between 3 and 16 characters long".
However, to generate this error, we need to know the range of validity in the view, which means duplicating knowledge. This is worsened when you have multiple front-ends, and the API serialises error codes instead of rendered messages due to i18n).
When the form changes, we need to remember to change the error message in the serialiser and/or probably in the web views if the app also has those.
It would be useful, then, to include context information about the failure in the error itself, so that clients that render this error have all the information from the single source of truth regarding validations: the Scrivener object.
The solution I used (which is probably not be the best, but it worked for me) was to introduce a Scrivener::Error object that emulated the error symbol, but added a context attribute. Essentially:
class Error < SimpleDelegator
attr_reader :context
def initialize(code, context = nil)
super(code)
@context = context
end
end
And then went and copied scrivener/validations into the project and replaced all the assertions with:
def assert_length(att, range, error = [att, Error.new(:not_in_range, range)])
…
end
This works, and particularly, kept compatibility with all our existing forms as the error "is still" a Symbol. However, seems a bit heavy handed to wrap an (extra) object around every error code.
While writing this issue, I thought of an alternative, by changing errors into 2-element pairs:
def assert_length(att, range, error = [att, [:not_in_range, range]])
…
end
def assert_url(att, error = [att, [:not_url]]) # or [:not_url, nil] to be explicit ¯\_(ツ)_/¯
…
end
This is pretty minimalistic, and a bit more memory efficient than the above solution, but would break everyone's code if implemented into Scrivener 😄
Is this a "problem" only for me?
We introduced this when a requirement changed and we had to wait for a new release of the mobile application to support these contexts in the API before we were able to implement the change in the business rule itself, which was a bit annoying, as mobile apps aren't particularly fast at upgrading 😛
Is this worth changing in Scrivener itself? Or is everyone fine with duplicating error contexts in wherever you generate error messages / serialise responses to your APIs / whatever.
Something I've run into often is that validation errors can't be fully compressed into a single code. A common enough case is validating length:
If the user's provided username was out of bounds, we'd get a
{ foo: [:not_in_range] }error. Now, the error message we want to render will most likely need to include the range in which it is valid. Something like "The username must be between 3 and 16 characters long".However, to generate this error, we need to know the range of validity in the view, which means duplicating knowledge. This is worsened when you have multiple front-ends, and the API serialises error codes instead of rendered messages due to i18n).
When the form changes, we need to remember to change the error message in the serialiser and/or probably in the web views if the app also has those.
It would be useful, then, to include context information about the failure in the error itself, so that clients that render this error have all the information from the single source of truth regarding validations: the Scrivener object.
The solution I used (which is probably not be the best, but it worked for me) was to introduce a
Scrivener::Errorobject that emulated the error symbol, but added a context attribute. Essentially:And then went and copied scrivener/validations into the project and replaced all the assertions with:
This works, and particularly, kept compatibility with all our existing forms as the error "is still" a Symbol. However, seems a bit heavy handed to wrap an (extra) object around every error code.
While writing this issue, I thought of an alternative, by changing errors into 2-element pairs:
This is pretty minimalistic, and a bit more memory efficient than the above solution, but would break everyone's code if implemented into Scrivener 😄
Is this a "problem" only for me?
We introduced this when a requirement changed and we had to wait for a new release of the mobile application to support these contexts in the API before we were able to implement the change in the business rule itself, which was a bit annoying, as mobile apps aren't particularly fast at upgrading 😛
Is this worth changing in Scrivener itself? Or is everyone fine with duplicating error contexts in wherever you generate error messages / serialise responses to your APIs / whatever.