-
-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Params validations #22
Comments
Also it just occurred me: should we consider using decorators instead of subclasses of class SomeAction
def call(params)
if some_condition_that_may_switch_the_parameter_format?
params.extend(SomeParamsRule)
else
params.extend(AnotherParamsRule)
end
end
end Just throwing out some ideas :) PS.: I think you meant |
@fuadsaud extending roles at the runtime is always a 👎 for me. Please read: http://tonyarcieri.com/dci-in-ruby-is-completely-broken What's the use case for that? The main goal of standalone actions is to reduce conditionals, by mapping use case to action in a 1:1 relationship. I'd love to hear more about this. |
I think the Option A is better becAuse can be reusable, what do you think? |
So, about dynamically extending objects: I know it is slower than plain decorators (I think the comparison may in the post is a little unfair because he compares a case where he extends the object with one where no extension happens at all, which doesn't solve the problem in question), and I really think decorators are best fit for tasks like this (I just pointed out it was possible to do that through mixins). About the params validation process itself: I guess you're right, it's better to encourage a more straightforward approach. I still think having the ability to compose different rules may be needed (I'm thinking about localized input like currencies, but I don't have a clear picture in my head of how that could work, I have to think more about that yet). |
I am giving this a try (option A for now) - how should we validate nested hashes? param :user do
param :name, String, presence: true
param :email, String, format: /.../
end It should also be possible to validate whether each item in a given array fits the given validations, although I am not sure how the syntax for that would look. param :a_bunch_of_names, String, each: { format: /.../ } What do you think? |
@AlfonsoUceda I was thinking of having a module MyCommonValidations
include Lotus::Validations
param :email, String, presence: true, format: /(.*)/
end
class Signup
include Lotus::Action
params do
extend MyCommonValidations
param :first_name, String, presence: true
param :last_name, String, presence: true
end
def call(params)
puts params.class # => Signup::Params
end
end |
@stsc3000 what if we do param :a_bunch_of_names, validator: MyValidator |
@jodosha I like that - this way a user can provide a custom |
@stsc3000 I assume that |
@jodosha class AttachmentBelongsToSite
def initialize(site)
@site = site
end
def call(params)
@site.attachments.exists? params[:attachment_id]
end
end and have this in controller # before_action callback
def validate_params
validator = ParamsValidator.new
validator.add_rule AttachmentBelongsToSite.new(current_site)
validator.validate!
end |
@vitaLee I like it. How do you use it? |
Oops I forgot this important detail, passing def validate_params
validator = ParamsValidator.new
validator.add_rule AttachmentBelongsToSite.new(current_site)
validator.validate!(params)
end I didn't make it clear but in case it didn't become clear, we're talking about Rails app here. # controller
rescue_from ParamsValidator::InvalidParamsError, with: :invalid_params
def invalid_params
head :unprocessable_entity
end ParamsValidator is iterating all validation rules it was supplied with and raises |
@vitaLee why do you add rules at the runtime instead of having a concrete implementation at the beginning? |
@jodosha In my case I had a base controller that supplied the validator with shared validation rules and allowed subcontrollers to append additional rules like this def validate_params
validator = ParamsValidator.new
validator.add_rule SharedValidationRule.new
validator.add_rule AnotherSharedValidationRule.new
configure_params_validator validator # allow subcontroller to add additional rules
validator.validate!(params)
end
def configure_params_validator(validator)
# noop in base controller but in subcontrollers we could append additional rules
end |
this way I can feed the validator with rules constructed with data specific to the context of the current request, and it's more transparent what rules I use in each subcontroller.
In my case I just didn't hit the point where I felt the need to subclass |
I've implemented the whitelisting portion of this in pull #31. It implements it as laid out in the "A" proposal. If I get the thumbs up on this part, I'll move on to the the coercion and validation parts. |
One unanswered question is: where this class of option "A" should be placed? Eg. My opinion is: we should use @mjbellantoni work on #31 (which implements A) and have option B as default. So that, we don't have to find a default place for concrete params classes. But developers who want to do otherwise can use |
I don't think it will be that hard to support both options, actually. I've almost got type coercions completed. From there I'll either move onto validations or DSL support as laid out in option "B". |
@mjbellantoni I'd like to have validations as a separated mixin. Please keep this in mind while design. I'm thinking to a |
For supplying data to validations (such as when checking for a unique value in a collection of entities, for example), it seems like you would have to have a way to pass a scope to the validator, like @vitaLee mentioned. What if controller exposures were made available to a validation object, similar to |
Maybe something like this: hanami/validations#2 |
@viking Uniqueness validators are a lie. For a long technical explanation, please read: http://robots.thoughtbot.com/the-perils-of-uniqueness-validations |
Fair enough. On August 5, 2014 5:01:01 PM CDT, Luca Guidi notifications@github.com wrote:
Sent from my Android device with K-9 Mail. Please excuse my brevity. |
@jodosha The article is great on explaining what might go wrong. But what about a solution? Imagine a user model with a I do a POST and submit duplicate content for both fields. Relying on constraint exceptions will only trigger the first found constraint violation. Resulting in only it invalidating the first field. The user has to submit it again with the modified username to get the second validation constraint. Why not both? Add a uniqueness check in the validations block and also handle constraint violations properly in case the race condition DOES happen. |
@sarahhenkens I believe you meant to comment on #232. |
@sarahhenkens Thanks for your input. 😄 Given the schema of a validation is totally decoupled from any entity/repository/database. How could be the syntax to indicate the uniqueness of a given field? params do
required(:user).schema do
required(:email).filled(:str?)
end
end In the case above |
The way I've implemented it in my project at the moment is: class Validator
include Hanami::Validations
predicate :unique?, message: "must be unique" do |field, current|
User.where(field => current).count == 0
end
predicate :email?, message: "must be an email" do |current|
current =~ /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
end
validations do
required(:email).filled(:str?, :email?) { unique?(:email) }
required(:username).filled(:str?) { unique?(:username) }
required(:password).filled(:str?)
end
end I've added a |
I've been doing roughly the same as @sarahhenkens. |
@sarahhenkens @beauby yup. Is your a proposal to have a standard predicate like that included in |
@jodosha I'd be in favor of an example in the docs, as providing a standard predicate would force too many assumptions. My main concern was that in #232, the discussion seemed to imply that the maintainers would label uniqueness validations as "bad practice", whereas it is kind of unavoidable in certain cases. |
@beauby the only problem that I have by adding to the guides is that everything documented there will eventually become the "Hanami way". Honestly I won't push for that uniqueness validation. WDYT? |
@jodosha I may be missing something but I don't see any other solution than uniqueness validations for informing the user about which field broke a uniqueness constraint (except when there's only one). Hence, I'm advocating for uniqueness validations to become the "Hanami way". FWIW, I remember discussing this with @solnic who held the same views. |
@beauby I agree that using exceptions for control flow isn't ideal. But what is worst is to rely on a race condition to validate data. I've seen too many Rails apps with an uniqueness validation without the corresponding database constraint. I just want to avoid this situation in Hanami projects. How to move on from this? I have a few suggestions:
|
Right now, the
params
passed to#call
are an instance ofLotus::Action::Params
which is a nice wrapper around a Ruby Hash.I would love to pass subclasses of
Params
which are aware of the specific use case and they should be able to do:#call
.This approach has the following advantages:
We probably need to add this interface:
And implement one of those solutions:
A
Where:
B
Where
.params
would generate an inner class of the actionSignup::Params
, which inherits fromLotus::Action::Params
.The text was updated successfully, but these errors were encountered: